Compare commits
8 Commits
use-renet-
...
0735c429ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0735c429ca | ||
|
|
c3c5ae6dfb | ||
|
|
cc7e2aae70 | ||
|
|
dbcd822b50 | ||
| ac8c834f2f | |||
| f35275ab9f | |||
| 3901ee1174 | |||
|
|
7d280af821 |
98
Cargo.lock
generated
98
Cargo.lock
generated
@@ -512,6 +512,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy-persistent"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e58d92d32bb99fa22ed46aeabe5212f5d1bc8952ebf9c49b5271fc06a1359f8"
|
||||||
|
dependencies = [
|
||||||
|
"bevy",
|
||||||
|
"gloo-storage",
|
||||||
|
"ron 0.11.0",
|
||||||
|
"serde",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy-steamworks"
|
name = "bevy-steamworks"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@@ -1359,25 +1372,6 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bevy_pkv"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "356a9c6fdc13faf7897103b43a8b84aafe24e1bbf1599df1fb00dc4e9b7055db"
|
|
||||||
dependencies = [
|
|
||||||
"bevy_app",
|
|
||||||
"bevy_ecs",
|
|
||||||
"cfg_aliases",
|
|
||||||
"directories",
|
|
||||||
"redb",
|
|
||||||
"rmp-serde",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_platform"
|
name = "bevy_platform"
|
||||||
version = "0.17.3"
|
version = "0.17.3"
|
||||||
@@ -2781,10 +2775,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "11277822c27bde750de02c5dc5159b91e88bf2661a2c1d98106f2fb1c5c6f590"
|
checksum = "11277822c27bde750de02c5dc5159b91e88bf2661a2c1d98106f2fb1c5c6f590"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "dirs"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
@@ -3473,6 +3467,34 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gloo-storage"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a"
|
||||||
|
dependencies = [
|
||||||
|
"gloo-utils",
|
||||||
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gloo-utils"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
@@ -3681,18 +3703,19 @@ dependencies = [
|
|||||||
"avian3d",
|
"avian3d",
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy-inspector-egui",
|
"bevy-inspector-egui",
|
||||||
|
"bevy-persistent",
|
||||||
"bevy-steamworks",
|
"bevy-steamworks",
|
||||||
"bevy_asset_loader",
|
"bevy_asset_loader",
|
||||||
"bevy_ballistic",
|
"bevy_ballistic",
|
||||||
"bevy_common_assets",
|
"bevy_common_assets",
|
||||||
"bevy_debug_log",
|
"bevy_debug_log",
|
||||||
"bevy_pkv",
|
|
||||||
"bevy_replicon",
|
"bevy_replicon",
|
||||||
"bevy_replicon_renet",
|
"bevy_replicon_renet",
|
||||||
"bevy_sprite3d",
|
"bevy_sprite3d",
|
||||||
"bevy_trenchbroom",
|
"bevy_trenchbroom",
|
||||||
"bevy_trenchbroom_avian",
|
"bevy_trenchbroom_avian",
|
||||||
"clap",
|
"clap",
|
||||||
|
"dirs",
|
||||||
"happy_feet",
|
"happy_feet",
|
||||||
"nil 0.14.0",
|
"nil 0.14.0",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@@ -5459,15 +5482,6 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb"
|
checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redb"
|
|
||||||
version = "3.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae323eb086579a3769daa2c753bb96deb95993c534711e0dbe881b5192906a06"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -5587,28 +5601,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmp"
|
|
||||||
version = "0.8.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"num-traits",
|
|
||||||
"paste",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmp-serde"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"rmp",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "robust"
|
name = "robust"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|||||||
@@ -51,15 +51,12 @@ bevy = { version = "0.17.0", default-features = false, features = [
|
|||||||
"track_location",
|
"track_location",
|
||||||
] }
|
] }
|
||||||
bevy-inspector-egui = "0.34"
|
bevy-inspector-egui = "0.34"
|
||||||
|
bevy-persistent = { version = "0.9", features = ["ron"] }
|
||||||
bevy-steamworks = "0.15.0"
|
bevy-steamworks = "0.15.0"
|
||||||
bevy_asset_loader = "=0.24.0-rc.1"
|
bevy_asset_loader = "=0.24.0-rc.1"
|
||||||
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", rev = "b08ffec" }
|
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", rev = "b08ffec" }
|
||||||
bevy_common_assets = { version = "0.14.0", features = ["ron"] }
|
bevy_common_assets = { version = "0.14.0", features = ["ron"] }
|
||||||
bevy_debug_log = { git = "https://github.com/rustunit/bevy_debug_log.git", rev = "86051a0" }
|
bevy_debug_log = { git = "https://github.com/rustunit/bevy_debug_log.git", rev = "86051a0" }
|
||||||
bevy_pkv = { version = "0.14", default-features = false, features = [
|
|
||||||
"bevy",
|
|
||||||
"redb",
|
|
||||||
] }
|
|
||||||
bevy_replicon = "0.37.1"
|
bevy_replicon = "0.37.1"
|
||||||
# TODO: i dont think we need this in dedicated server mode
|
# TODO: i dont think we need this in dedicated server mode
|
||||||
bevy_replicon_renet = { version = "0.13.0", features = ["renet_steam"] }
|
bevy_replicon_renet = { version = "0.13.0", features = ["renet_steam"] }
|
||||||
@@ -69,6 +66,7 @@ bevy_trenchbroom = { version = "0.10", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
bevy_trenchbroom_avian = "0.10"
|
bevy_trenchbroom_avian = "0.10"
|
||||||
clap = { version = "=4.5.47", features = ["derive"] }
|
clap = { version = "=4.5.47", features = ["derive"] }
|
||||||
|
dirs = "6.0.0"
|
||||||
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "919657fa", features = [
|
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "919657fa", features = [
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -26,18 +26,19 @@ dbg = ["avian3d/debug-plugin", "bevy/debug", "dep:bevy-inspector-egui"]
|
|||||||
avian3d = { workspace = true }
|
avian3d = { workspace = true }
|
||||||
bevy = { workspace = true }
|
bevy = { workspace = true }
|
||||||
bevy-inspector-egui = { workspace = true, optional = true }
|
bevy-inspector-egui = { workspace = true, optional = true }
|
||||||
|
bevy-persistent = { workspace = true }
|
||||||
bevy-steamworks = { workspace = true }
|
bevy-steamworks = { workspace = true }
|
||||||
bevy_asset_loader = { workspace = true }
|
bevy_asset_loader = { workspace = true }
|
||||||
bevy_ballistic = { workspace = true }
|
bevy_ballistic = { workspace = true }
|
||||||
bevy_common_assets = { workspace = true }
|
bevy_common_assets = { workspace = true }
|
||||||
bevy_debug_log = { workspace = true }
|
bevy_debug_log = { workspace = true }
|
||||||
bevy_pkv = { workspace = true }
|
|
||||||
bevy_replicon = { workspace = true }
|
bevy_replicon = { workspace = true }
|
||||||
bevy_replicon_renet = { workspace = true }
|
bevy_replicon_renet = { workspace = true }
|
||||||
bevy_sprite3d = { workspace = true }
|
bevy_sprite3d = { workspace = true }
|
||||||
bevy_trenchbroom = { workspace = true }
|
bevy_trenchbroom = { workspace = true }
|
||||||
bevy_trenchbroom_avian = { workspace = true }
|
bevy_trenchbroom_avian = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
dirs = { workspace = true }
|
||||||
happy_feet = { workspace = true }
|
happy_feet = { workspace = true }
|
||||||
nil = { workspace = true }
|
nil = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
mod marker;
|
|
||||||
mod target_ui;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, control::Inputs, head::ActiveHead, heads_database::HeadsDatabase,
|
GameState,
|
||||||
hitpoints::Hitpoints, physics_layers::GameLayer, player::Player, tb_entities::EnemySpawn,
|
control::Inputs,
|
||||||
|
head::ActiveHead,
|
||||||
|
heads_database::HeadsDatabase,
|
||||||
|
hitpoints::Hitpoints,
|
||||||
|
physics_layers::GameLayer,
|
||||||
|
player::{LocalPlayer, Player},
|
||||||
|
tb_entities::EnemySpawn,
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use marker::MarkerEvent;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
@@ -21,7 +23,12 @@ pub struct AimTarget(pub Option<Entity>);
|
|||||||
pub struct AimState {
|
pub struct AimState {
|
||||||
pub range: f32,
|
pub range: f32,
|
||||||
pub max_angle: f32,
|
pub max_angle: f32,
|
||||||
pub spawn_marker: bool,
|
}
|
||||||
|
|
||||||
|
#[derive(Event)]
|
||||||
|
pub enum MarkerEvent {
|
||||||
|
Spawn(Entity),
|
||||||
|
Despawn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AimState {
|
impl Default for AimState {
|
||||||
@@ -29,7 +36,6 @@ impl Default for AimState {
|
|||||||
Self {
|
Self {
|
||||||
range: 80.,
|
range: 80.,
|
||||||
max_angle: PI / 8.,
|
max_angle: PI / 8.,
|
||||||
spawn_marker: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,20 +44,12 @@ pub fn plugin(app: &mut App) {
|
|||||||
app.register_type::<AimState>();
|
app.register_type::<AimState>();
|
||||||
app.register_type::<AimTarget>();
|
app.register_type::<AimTarget>();
|
||||||
|
|
||||||
app.add_plugins(target_ui::plugin);
|
app.register_required_components::<ActiveHead, AimState>();
|
||||||
app.add_plugins(marker::plugin);
|
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(update_player_aim, update_npc_aim, head_change).run_if(in_state(GameState::Playing)),
|
(update_player_aim, update_npc_aim, head_change).run_if(in_state(GameState::Playing)),
|
||||||
);
|
);
|
||||||
app.add_systems(Update, add_aim);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_aim(mut commands: Commands, query: Query<Entity, Added<ActiveHead>>) {
|
|
||||||
for e in query.iter() {
|
|
||||||
commands.entity(e).insert(AimState::default());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_change(
|
fn head_change(
|
||||||
@@ -70,12 +68,19 @@ fn update_player_aim(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
potential_targets: Query<(Entity, &Transform), With<Hitpoints>>,
|
potential_targets: Query<(Entity, &Transform), With<Hitpoints>>,
|
||||||
mut player_aim: Query<
|
mut player_aim: Query<
|
||||||
(Entity, &AimState, &mut AimTarget, &GlobalTransform, &Inputs),
|
(
|
||||||
|
Entity,
|
||||||
|
&AimState,
|
||||||
|
&mut AimTarget,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Inputs,
|
||||||
|
Has<LocalPlayer>,
|
||||||
|
),
|
||||||
With<Player>,
|
With<Player>,
|
||||||
>,
|
>,
|
||||||
spatial_query: SpatialQuery,
|
spatial_query: SpatialQuery,
|
||||||
) {
|
) {
|
||||||
for (player, state, mut aim_target, global_tf, inputs) in player_aim.iter_mut() {
|
for (player, state, mut aim_target, global_tf, inputs, is_local) in player_aim.iter_mut() {
|
||||||
let (player_pos, player_forward) = (global_tf.translation(), inputs.look_dir);
|
let (player_pos, player_forward) = (global_tf.translation(), inputs.look_dir);
|
||||||
|
|
||||||
let mut new_target = None;
|
let mut new_target = None;
|
||||||
@@ -114,13 +119,14 @@ fn update_player_aim(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if new_target != aim_target.0 {
|
if new_target != aim_target.0 {
|
||||||
if state.spawn_marker {
|
if is_local {
|
||||||
if let Some(target) = new_target {
|
if let Some(target) = new_target {
|
||||||
commands.trigger(MarkerEvent::Spawn(target));
|
commands.trigger(MarkerEvent::Spawn(target));
|
||||||
} else {
|
} else {
|
||||||
commands.trigger(MarkerEvent::Despawn);
|
commands.trigger(MarkerEvent::Despawn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aim_target.0 = new_target;
|
aim_target.0 = new_target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::GameState;
|
use crate::GameState;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::control::Inputs;
|
use crate::control::Inputs;
|
||||||
|
use crate::control::ViewMode;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::physics_layers::GameLayer;
|
use crate::physics_layers::GameLayer;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
@@ -31,7 +32,7 @@ pub struct CameraRotationInput(pub Vec2);
|
|||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct CameraState {
|
pub struct CameraState {
|
||||||
pub cutscene: bool,
|
pub cutscene: bool,
|
||||||
pub look_around: bool,
|
pub view_mode: ViewMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug, Default)]
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
@@ -85,10 +86,10 @@ fn update_look_around(
|
|||||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||||
mut cam_state: ResMut<CameraState>,
|
mut cam_state: ResMut<CameraState>,
|
||||||
) {
|
) {
|
||||||
let look_around = inputs.view_mode;
|
let view_mode = inputs.view_mode;
|
||||||
|
|
||||||
if look_around != cam_state.look_around {
|
if view_mode != cam_state.view_mode {
|
||||||
cam_state.look_around = look_around;
|
cam_state.view_mode = view_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +101,9 @@ fn update_ui(
|
|||||||
query: Query<Entity, With<CameraUi>>,
|
query: Query<Entity, With<CameraUi>>,
|
||||||
) {
|
) {
|
||||||
if cam_state.is_changed() {
|
if cam_state.is_changed() {
|
||||||
let show_ui = cam_state.look_around || cam_state.cutscene;
|
let show_free_cam_ui = cam_state.view_mode.is_free() || cam_state.cutscene;
|
||||||
|
|
||||||
if show_ui {
|
if show_free_cam_ui {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
CameraUi,
|
CameraUi,
|
||||||
Node {
|
Node {
|
||||||
@@ -131,12 +132,19 @@ fn update_ui(
|
|||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
fn update(
|
fn update(
|
||||||
mut cam: Query<
|
cam: Single<
|
||||||
(&MainCamera, &mut Transform, &CameraRotationInput),
|
(&MainCamera, &mut Transform, &CameraRotationInput),
|
||||||
(Without<CameraTarget>, Without<CameraArmRotation>),
|
(Without<CameraTarget>, Without<CameraArmRotation>),
|
||||||
>,
|
>,
|
||||||
target_q: Single<&Transform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
target_q: Single<
|
||||||
arm_rotation: Single<&Transform, With<CameraArmRotation>>,
|
(&Transform, &Children),
|
||||||
|
(
|
||||||
|
With<CameraTarget>,
|
||||||
|
With<LocalPlayer>,
|
||||||
|
Without<CameraArmRotation>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
|
||||||
spatial_query: SpatialQuery,
|
spatial_query: SpatialQuery,
|
||||||
cam_state: Res<CameraState>,
|
cam_state: Res<CameraState>,
|
||||||
) {
|
) {
|
||||||
@@ -144,11 +152,14 @@ fn update(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let arm_tf = arm_rotation;
|
let (camera, mut cam_transform, cam_rotation_input) = cam.into_inner();
|
||||||
|
|
||||||
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.single_mut() else {
|
let (target_q, children) = target_q.into_inner();
|
||||||
return;
|
|
||||||
};
|
let arm_tf = children
|
||||||
|
.iter()
|
||||||
|
.find_map(|child| arm_rotation.get(child).ok())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if !camera.enabled {
|
if !camera.enabled {
|
||||||
return;
|
return;
|
||||||
@@ -184,7 +195,7 @@ fn rotate_view(
|
|||||||
look_dir: Res<LookDirMovement>,
|
look_dir: Res<LookDirMovement>,
|
||||||
mut cam: Single<&mut CameraRotationInput>,
|
mut cam: Single<&mut CameraRotationInput>,
|
||||||
) {
|
) {
|
||||||
if !inputs.view_mode {
|
if !inputs.view_mode.is_free() {
|
||||||
cam.x = 0.0;
|
cam.x = 0.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
8
crates/hedz_reloaded/src/client/aim.rs
Normal file
8
crates/hedz_reloaded/src/client/aim.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub mod marker;
|
||||||
|
pub mod target_ui;
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.add_plugins((marker::plugin, target_ui::plugin));
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{GameState, global_observer, loading_assets::UIAssets, utils::billboards::Billboard};
|
use crate::{
|
||||||
|
GameState, aim::MarkerEvent, global_observer, loading_assets::UIAssets,
|
||||||
|
utils::billboards::Billboard,
|
||||||
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_sprite3d::Sprite3d;
|
use bevy_sprite3d::Sprite3d;
|
||||||
use ops::sin;
|
use ops::sin;
|
||||||
@@ -7,12 +10,6 @@ use ops::sin;
|
|||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct TargetMarker;
|
struct TargetMarker;
|
||||||
|
|
||||||
#[derive(Event)]
|
|
||||||
pub enum MarkerEvent {
|
|
||||||
Spawn(Entity),
|
|
||||||
Despawn,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Update, move_marker.run_if(in_state(GameState::Playing)));
|
app.add_systems(Update, move_marker.run_if(in_state(GameState::Playing)));
|
||||||
global_observer!(app, marker_event);
|
global_observer!(app, marker_event);
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
use super::AimTarget;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
|
aim::AimTarget,
|
||||||
backpack::UiHeadState,
|
backpack::UiHeadState,
|
||||||
heads::{ActiveHeads, HeadsImages},
|
heads::{ActiveHeads, HeadsImages},
|
||||||
hitpoints::Hitpoints,
|
hitpoints::Hitpoints,
|
||||||
loading_assets::UIAssets,
|
loading_assets::UIAssets,
|
||||||
npc::Npc,
|
npc::Npc,
|
||||||
player::Player,
|
player::LocalPlayer,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
@@ -18,12 +18,14 @@ struct HeadImage;
|
|||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct HeadDamage;
|
struct HeadDamage;
|
||||||
|
|
||||||
#[derive(Resource, Default, PartialEq)]
|
#[derive(Component, Default, PartialEq)]
|
||||||
struct TargetUi {
|
pub struct TargetUi {
|
||||||
head: Option<UiHeadState>,
|
head: Option<UiHeadState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.register_required_components::<LocalPlayer, TargetUi>();
|
||||||
|
|
||||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
app.add_systems(OnEnter(GameState::Playing), setup);
|
||||||
app.add_systems(Update, (sync, update).run_if(in_state(GameState::Playing)));
|
app.add_systems(Update, (sync, update).run_if(in_state(GameState::Playing)));
|
||||||
}
|
}
|
||||||
@@ -44,8 +46,6 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
assets.head_damage.clone(),
|
assets.head_damage.clone(),
|
||||||
)],
|
)],
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.insert_resource(TargetUi::default());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_head_ui(bg: Handle<Image>, regular: Handle<Image>, damage: Handle<Image>) -> impl Bundle {
|
fn spawn_head_ui(bg: Handle<Image>, regular: Handle<Image>, damage: Handle<Image>) -> impl Bundle {
|
||||||
@@ -110,7 +110,7 @@ fn spawn_head_ui(bg: Handle<Image>, regular: Handle<Image>, damage: Handle<Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
target: Res<TargetUi>,
|
target: Single<&TargetUi, (Changed<TargetUi>, With<LocalPlayer>)>,
|
||||||
heads_images: Res<HeadsImages>,
|
heads_images: Res<HeadsImages>,
|
||||||
mut head_image: Query<
|
mut head_image: Query<
|
||||||
(&mut Visibility, &mut ImageNode),
|
(&mut Visibility, &mut ImageNode),
|
||||||
@@ -118,7 +118,6 @@ 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 let Ok((mut vis, mut image)) = head_image.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;
|
||||||
@@ -131,17 +130,16 @@ fn update(
|
|||||||
if let Ok(mut node) = head_damage.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.);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync(
|
fn sync(
|
||||||
mut target: ResMut<TargetUi>,
|
mut target: Single<&mut TargetUi, With<LocalPlayer>>,
|
||||||
player_target: Query<&AimTarget, With<Player>>,
|
player_target: Single<&AimTarget, With<LocalPlayer>>,
|
||||||
target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>,
|
target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>,
|
||||||
) {
|
) {
|
||||||
let mut new_state = None;
|
let mut new_state = None;
|
||||||
if let Some(e) = player_target.iter().next().and_then(|target| target.0)
|
if let Some(target) = player_target.0
|
||||||
&& let Ok((hp, heads)) = target_data.get(e)
|
&& let Ok((hp, heads)) = target_data.get(target)
|
||||||
{
|
{
|
||||||
let head = heads.current().expect("target must have a head on");
|
let head = heads.current().expect("target must have a head on");
|
||||||
new_state = Some(UiHeadState {
|
new_state = Some(UiHeadState {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
control::{ControllerSet, Inputs, LookDirMovement},
|
control::{ControllerSet, Inputs, LookDirMovement, SelectedController},
|
||||||
player::{LocalPlayer, PlayerBodyMesh},
|
player::{LocalPlayer, PlayerBodyMesh},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -19,14 +19,20 @@ pub fn plugin(app: &mut App) {
|
|||||||
fn rotate_rig(
|
fn rotate_rig(
|
||||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||||
look_dir: Res<LookDirMovement>,
|
look_dir: Res<LookDirMovement>,
|
||||||
local_player: Single<&Children, With<LocalPlayer>>,
|
local_player: Single<(&Children, &SelectedController), With<LocalPlayer>>,
|
||||||
mut player_mesh: Query<&mut Transform, With<PlayerBodyMesh>>,
|
mut player_mesh: Query<&mut Transform, With<PlayerBodyMesh>>,
|
||||||
) {
|
) {
|
||||||
if inputs.view_mode {
|
if inputs.view_mode.is_free() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_player.iter().find(|&child| {
|
let (local_player_childer, selected_controller) = *local_player;
|
||||||
|
|
||||||
|
if !matches!(selected_controller, SelectedController::Flying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_player_childer.iter().find(|&child| {
|
||||||
if let Ok(mut rig_transform) = player_mesh.get_mut(child) {
|
if let Ok(mut rig_transform) = player_mesh.get_mut(child) {
|
||||||
let look_dir = look_dir.0;
|
let look_dir = look_dir.0;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
client::control::CharacterInputEnabled,
|
client::control::CharacterInputEnabled,
|
||||||
control::{
|
control::{
|
||||||
BackpackButtonPress, CashHealPressed, ClientInputs, ControllerSet, Inputs, LocalInputs,
|
BackpackButtonPress, CashHealPressed, ClientInputs, ControllerSet, Inputs, LocalInputs,
|
||||||
LookDirMovement, SelectLeftPressed, SelectRightPressed,
|
LookDirMovement, SelectLeftPressed, SelectRightPressed, ViewMode,
|
||||||
},
|
},
|
||||||
player::{LocalPlayer, PlayerBodyMesh},
|
player::{LocalPlayer, PlayerBodyMesh},
|
||||||
};
|
};
|
||||||
@@ -85,9 +85,14 @@ fn reset_control_state_on_disable(
|
|||||||
|
|
||||||
fn get_lookdir(
|
fn get_lookdir(
|
||||||
mut inputs: Single<&mut LocalInputs>,
|
mut inputs: Single<&mut LocalInputs>,
|
||||||
rig_transform: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
player: Single<&Children, With<LocalPlayer>>,
|
||||||
|
rig_transform: Query<&GlobalTransform, With<PlayerBodyMesh>>,
|
||||||
) {
|
) {
|
||||||
inputs.0.look_dir = if let Some(ref rig_transform) = rig_transform {
|
let rig_transform = player
|
||||||
|
.iter()
|
||||||
|
.find_map(|child| rig_transform.get(child).ok());
|
||||||
|
|
||||||
|
inputs.0.look_dir = if let Some(rig_transform) = rig_transform {
|
||||||
rig_transform.forward().as_vec3()
|
rig_transform.forward().as_vec3()
|
||||||
} else {
|
} else {
|
||||||
Vec3::NEG_Z
|
Vec3::NEG_Z
|
||||||
@@ -141,8 +146,11 @@ fn gamepad_controls(
|
|||||||
|
|
||||||
inputs.0.move_dir += move_dir.clamp_length_max(1.0);
|
inputs.0.move_dir += move_dir.clamp_length_max(1.0);
|
||||||
inputs.0.jump |= gamepad.pressed(GamepadButton::South);
|
inputs.0.jump |= gamepad.pressed(GamepadButton::South);
|
||||||
inputs.0.view_mode |= gamepad.pressed(GamepadButton::LeftTrigger2);
|
|
||||||
inputs.0.trigger |= gamepad.pressed(GamepadButton::RightTrigger2);
|
inputs.0.trigger |= gamepad.pressed(GamepadButton::RightTrigger2);
|
||||||
|
inputs
|
||||||
|
.0
|
||||||
|
.view_mode
|
||||||
|
.merge_input(gamepad.pressed(GamepadButton::LeftTrigger2));
|
||||||
|
|
||||||
if gamepad.just_pressed(GamepadButton::DPadUp) {
|
if gamepad.just_pressed(GamepadButton::DPadUp) {
|
||||||
backpack_inputs.write(BackpackButtonPress::Toggle);
|
backpack_inputs.write(BackpackButtonPress::Toggle);
|
||||||
@@ -207,8 +215,8 @@ fn keyboard_controls(
|
|||||||
|
|
||||||
inputs.0.move_dir = direction;
|
inputs.0.move_dir = direction;
|
||||||
inputs.0.jump = keyboard.pressed(KeyCode::Space);
|
inputs.0.jump = keyboard.pressed(KeyCode::Space);
|
||||||
inputs.0.view_mode = keyboard.pressed(KeyCode::Tab);
|
|
||||||
inputs.0.trigger = mouse.pressed(MouseButton::Left);
|
inputs.0.trigger = mouse.pressed(MouseButton::Left);
|
||||||
|
inputs.0.view_mode = ViewMode::from_input(keyboard.pressed(KeyCode::Tab));
|
||||||
|
|
||||||
if keyboard.just_pressed(KeyCode::KeyB) {
|
if keyboard.just_pressed(KeyCode::KeyB) {
|
||||||
backpack_inputs.write(BackpackButtonPress::Toggle);
|
backpack_inputs.write(BackpackButtonPress::Toggle);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use bevy_replicon_renet::{
|
|||||||
use bevy_steamworks::Client;
|
use bevy_steamworks::Client;
|
||||||
use bevy_trenchbroom::geometry::Brushes;
|
use bevy_trenchbroom::geometry::Brushes;
|
||||||
|
|
||||||
|
pub mod aim;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod backpack;
|
pub mod backpack;
|
||||||
pub mod control;
|
pub mod control;
|
||||||
@@ -36,6 +37,8 @@ pub mod ui;
|
|||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
|
aim::plugin,
|
||||||
|
audio::plugin,
|
||||||
backpack::plugin,
|
backpack::plugin,
|
||||||
control::plugin,
|
control::plugin,
|
||||||
debug::plugin,
|
debug::plugin,
|
||||||
@@ -43,7 +46,6 @@ pub fn plugin(app: &mut App) {
|
|||||||
heal_effect::plugin,
|
heal_effect::plugin,
|
||||||
player::plugin,
|
player::plugin,
|
||||||
setup::plugin,
|
setup::plugin,
|
||||||
audio::plugin,
|
|
||||||
steam::plugin,
|
steam::plugin,
|
||||||
ui::plugin,
|
ui::plugin,
|
||||||
settings::plugin,
|
settings::plugin,
|
||||||
@@ -125,7 +127,7 @@ fn connect_to_server(
|
|||||||
let authentication = bevy_replicon_renet::netcode::ClientAuthentication::Unsecure {
|
let authentication = bevy_replicon_renet::netcode::ClientAuthentication::Unsecure {
|
||||||
client_id,
|
client_id,
|
||||||
protocol_id: 0,
|
protocol_id: 0,
|
||||||
server_addr: host_addr.clone(),
|
server_addr: *host_addr,
|
||||||
user_data: None,
|
user_data: None,
|
||||||
};
|
};
|
||||||
let transport = bevy_replicon_renet::netcode::NetcodeClientTransport::new(
|
let transport = bevy_replicon_renet::netcode::NetcodeClientTransport::new(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
global_observer,
|
global_observer,
|
||||||
heads_database::{HeadControls, HeadsDatabase},
|
heads_database::{HeadControls, HeadsDatabase},
|
||||||
loading_assets::AudioAssets,
|
loading_assets::AudioAssets,
|
||||||
player::{LocalPlayer, PlayerBodyMesh},
|
player::{LocalPlayer, Player, PlayerBodyMesh},
|
||||||
protocol::{ClientHeadChanged, PlaySound, PlayerId, messages::AssignClientPlayer},
|
protocol::{ClientHeadChanged, PlaySound, PlayerId, messages::AssignClientPlayer},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -59,23 +59,38 @@ pub enum PlayerAssignmentState {
|
|||||||
Confirmed,
|
Confirmed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: currently a networked message.
|
||||||
|
// can be done by just using local change detection on `ActiveHead`?
|
||||||
fn on_client_update_head_mesh(
|
fn on_client_update_head_mesh(
|
||||||
trigger: On<ClientHeadChanged>,
|
trigger: On<ClientHeadChanged>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
body_mesh: Single<(Entity, &Children), With<PlayerBodyMesh>>,
|
player: Query<(&Children, &PlayerId), With<Player>>,
|
||||||
|
body_mesh: Query<(Entity, &Children), With<PlayerBodyMesh>>,
|
||||||
head_db: Res<HeadsDatabase>,
|
head_db: Res<HeadsDatabase>,
|
||||||
audio_assets: Res<AudioAssets>,
|
audio_assets: Res<AudioAssets>,
|
||||||
sfx: Query<&AudioPlayer>,
|
sfx: Query<&AudioPlayer>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let head = trigger.0 as usize;
|
let (player_children, _) = player
|
||||||
let (body_mesh, mesh_children) = *body_mesh;
|
.iter()
|
||||||
|
.find(|(_, player_id)| **player_id == trigger.player)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (body_mesh, body_mesh_children) = player_children
|
||||||
|
.iter()
|
||||||
|
.find_map(|child| body_mesh.get(child).ok())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let head = trigger.head;
|
||||||
|
|
||||||
let head_str = head_db.head_key(head);
|
let head_str = head_db.head_key(head);
|
||||||
|
|
||||||
commands.trigger(PlaySound::Head(head_str.to_string()));
|
commands.trigger(PlaySound::Head(head_str.to_string()));
|
||||||
|
|
||||||
//TODO: make part of full character mesh later
|
//TODO: make part of full character mesh later
|
||||||
for child in mesh_children.iter().filter(|child| sfx.contains(*child)) {
|
for child in body_mesh_children
|
||||||
|
.iter()
|
||||||
|
.filter(|child| sfx.contains(*child))
|
||||||
|
{
|
||||||
commands.entity(child).despawn();
|
commands.entity(child).despawn();
|
||||||
}
|
}
|
||||||
if head_db.head_stats(head).controls == HeadControls::Plane {
|
if head_db.head_stats(head).controls == HeadControls::Plane {
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
use bevy_pkv::prelude::*;
|
|
||||||
|
|
||||||
use crate::{client::audio::SoundSettings, utils::Debounce};
|
use crate::{client::audio::SoundSettings, utils::Debounce};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_persistent::{Persistent, StorageFormat};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
#[cfg(not(feature = "dbg"))]
|
app.insert_resource(
|
||||||
app.insert_resource(PkvStore::new("Rustunit", "HEDZ"));
|
Persistent::<SoundSettings>::builder()
|
||||||
|
.name("audio")
|
||||||
|
.format(StorageFormat::Ron)
|
||||||
|
.path(
|
||||||
|
dirs::config_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join("com.rustunit.hedzreloaded")
|
||||||
|
.join("audio.ron"),
|
||||||
|
)
|
||||||
|
.default(SoundSettings::default())
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
app.add_systems(Update, persist_settings.run_if(resource_exists::<PkvStore>));
|
app.add_systems(Update, persist_settings);
|
||||||
app.add_systems(Startup, load_settings.run_if(resource_exists::<PkvStore>));
|
app.add_systems(Startup, load_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn persist_settings(
|
fn persist_settings(
|
||||||
settings: Res<SoundSettings>,
|
settings: Res<SoundSettings>,
|
||||||
mut pkv: ResMut<PkvStore>,
|
mut persistent: ResMut<Persistent<SoundSettings>>,
|
||||||
mut debounce: Debounce<1000>,
|
mut debounce: Debounce<1000>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if settings.is_changed() {
|
if settings.is_changed() {
|
||||||
@@ -21,16 +32,12 @@ fn persist_settings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if debounce.finished() {
|
if debounce.finished() {
|
||||||
pkv.set("audio", &*settings)?;
|
persistent.set(*settings)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_settings(mut settings: ResMut<SoundSettings>, pkv: Res<PkvStore>) -> Result {
|
fn load_settings(persistent: Res<Persistent<SoundSettings>>, mut settings: ResMut<SoundSettings>) {
|
||||||
if let Ok(loaded) = pkv.get::<SoundSettings>("audio") {
|
*settings = *persistent.get();
|
||||||
*settings = loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use steamworks::SteamId;
|
use steamworks::SteamId;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
@@ -81,8 +80,7 @@ impl From<NetworkingConfig> for NetConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_addr(addr: Option<String>) -> SocketAddr {
|
fn parse_addr(addr: Option<String>) -> SocketAddr {
|
||||||
addr.map(|addr| addr.parse().ok())
|
addr.and_then(|addr| addr.parse().ok())
|
||||||
.flatten()
|
|
||||||
.unwrap_or_else(|| "127.0.0.1:31111".parse().unwrap())
|
.unwrap_or_else(|| "127.0.0.1:31111".parse().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,12 +72,22 @@ fn set_animation_flags(
|
|||||||
pub fn reset_upon_switch(
|
pub fn reset_upon_switch(
|
||||||
mut c: Commands,
|
mut c: Commands,
|
||||||
mut event_controller_switch: MessageReader<ControllerSwitchEvent>,
|
mut event_controller_switch: MessageReader<ControllerSwitchEvent>,
|
||||||
selected_controller: Res<SelectedController>,
|
|
||||||
mut rig_transforms: Query<&mut Transform, With<PlayerBodyMesh>>,
|
mut rig_transforms: Query<&mut Transform, With<PlayerBodyMesh>>,
|
||||||
mut controllers: Query<(&mut KinematicVelocity, &Children, &Inputs), With<Player>>,
|
mut controllers: Query<
|
||||||
|
(
|
||||||
|
&mut KinematicVelocity,
|
||||||
|
&Children,
|
||||||
|
&Inputs,
|
||||||
|
&SelectedController,
|
||||||
|
),
|
||||||
|
With<Player>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
for &ControllerSwitchEvent { controller } in event_controller_switch.read() {
|
for &ControllerSwitchEvent { controller } in event_controller_switch.read() {
|
||||||
let (mut velocity, children, inputs) = controllers.get_mut(controller).unwrap();
|
let (mut velocity, children, inputs, selected_controller) =
|
||||||
|
controllers.get_mut(controller).unwrap();
|
||||||
|
|
||||||
|
info!("resetting controller");
|
||||||
|
|
||||||
velocity.0 = Vec3::ZERO;
|
velocity.0 = Vec3::ZERO;
|
||||||
|
|
||||||
@@ -165,6 +175,7 @@ impl Default for MovementSpeedFactor {
|
|||||||
MoveInput,
|
MoveInput,
|
||||||
MovementSpeedFactor,
|
MovementSpeedFactor,
|
||||||
TransformInterpolation,
|
TransformInterpolation,
|
||||||
|
SelectedController::Running,
|
||||||
CharacterMovement = RUNNING_MOVEMENT_CONFIG.movement,
|
CharacterMovement = RUNNING_MOVEMENT_CONFIG.movement,
|
||||||
ControllerSettings = RUNNING_MOVEMENT_CONFIG.settings,
|
ControllerSettings = RUNNING_MOVEMENT_CONFIG.settings,
|
||||||
CharacterGravity = RUNNING_MOVEMENT_CONFIG.gravity,
|
CharacterGravity = RUNNING_MOVEMENT_CONFIG.gravity,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::ControllerSet;
|
use super::ControllerSet;
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
control::{Inputs, controller_common::MovementSpeedFactor},
|
control::{Inputs, SelectedController, controller_common::MovementSpeedFactor},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use happy_feet::prelude::MoveInput;
|
use happy_feet::prelude::MoveInput;
|
||||||
@@ -19,8 +19,17 @@ impl Plugin for CharacterControllerPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_controls(character: Single<(&mut MoveInput, &MovementSpeedFactor, &Inputs)>) {
|
pub fn apply_controls(
|
||||||
let (mut char_input, factor, inputs) = character.into_inner();
|
mut query: Query<(
|
||||||
|
&mut MoveInput,
|
||||||
char_input.set(inputs.look_dir * factor.0);
|
&MovementSpeedFactor,
|
||||||
|
&Inputs,
|
||||||
|
&SelectedController,
|
||||||
|
)>,
|
||||||
|
) {
|
||||||
|
for (mut move_input, factor, inputs, selected_controller) in query.iter_mut() {
|
||||||
|
if *selected_controller == SelectedController::Flying {
|
||||||
|
move_input.set(inputs.look_dir * factor.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
animation::AnimationFlags,
|
animation::AnimationFlags,
|
||||||
control::{ControllerSet, ControllerSettings, Inputs, controller_common::MovementSpeedFactor},
|
control::{
|
||||||
|
ControllerSet, ControllerSettings, Inputs, SelectedController,
|
||||||
|
controller_common::MovementSpeedFactor,
|
||||||
|
},
|
||||||
protocol::is_server,
|
protocol::is_server,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
@@ -41,7 +44,7 @@ fn rotate_view(
|
|||||||
) {
|
) {
|
||||||
let (inputs, children) = controller.into_inner();
|
let (inputs, children) = controller.into_inner();
|
||||||
|
|
||||||
if inputs.view_mode {
|
if inputs.view_mode.is_free() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +59,7 @@ fn rotate_view(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_controls(
|
fn apply_controls(
|
||||||
character: Single<(
|
mut query: Query<(
|
||||||
&mut MoveInput,
|
&mut MoveInput,
|
||||||
&mut Grounding,
|
&mut Grounding,
|
||||||
&mut KinematicVelocity,
|
&mut KinematicVelocity,
|
||||||
@@ -64,10 +67,23 @@ fn apply_controls(
|
|||||||
&ControllerSettings,
|
&ControllerSettings,
|
||||||
&MovementSpeedFactor,
|
&MovementSpeedFactor,
|
||||||
&Inputs,
|
&Inputs,
|
||||||
|
&SelectedController,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
let (mut move_input, mut grounding, mut velocity, mut flags, settings, move_factor, inputs) =
|
for (
|
||||||
character.into_inner();
|
mut move_input,
|
||||||
|
mut grounding,
|
||||||
|
mut velocity,
|
||||||
|
mut flags,
|
||||||
|
settings,
|
||||||
|
move_factor,
|
||||||
|
inputs,
|
||||||
|
selected_controller,
|
||||||
|
) in query.iter_mut()
|
||||||
|
{
|
||||||
|
if *selected_controller != SelectedController::Running {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let ground_normal = *grounding.normal().unwrap_or(Dir3::Y);
|
let ground_normal = *grounding.normal().unwrap_or(Dir3::Y);
|
||||||
|
|
||||||
@@ -85,4 +101,5 @@ fn apply_controls(
|
|||||||
flags.jump_count += 1;
|
flags.jump_count += 1;
|
||||||
happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y)
|
happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ pub enum ControllerSet {
|
|||||||
ApplyControlsRun,
|
ApplyControlsRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Component, Reflect, Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
pub enum SelectedController {
|
pub enum SelectedController {
|
||||||
Flying,
|
Flying,
|
||||||
#[default]
|
#[default]
|
||||||
@@ -35,8 +36,9 @@ pub fn plugin(app: &mut App) {
|
|||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
app.register_type::<LocalInputs>();
|
app.register_type::<LocalInputs>();
|
||||||
|
|
||||||
|
app.register_type::<SelectedController>();
|
||||||
|
|
||||||
app.init_resource::<LookDirMovement>();
|
app.init_resource::<LookDirMovement>();
|
||||||
app.init_resource::<SelectedController>();
|
|
||||||
|
|
||||||
app.add_message::<ControllerSwitchEvent>()
|
app.add_message::<ControllerSwitchEvent>()
|
||||||
.add_message::<BackpackButtonPress>();
|
.add_message::<BackpackButtonPress>();
|
||||||
@@ -48,8 +50,8 @@ pub fn plugin(app: &mut App) {
|
|||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
FixedUpdate,
|
FixedUpdate,
|
||||||
(
|
(
|
||||||
ControllerSet::ApplyControlsFly.run_if(resource_equals(SelectedController::Flying)),
|
ControllerSet::ApplyControlsFly,
|
||||||
ControllerSet::ApplyControlsRun.run_if(resource_equals(SelectedController::Running)),
|
ControllerSet::ApplyControlsRun,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.run_if(in_state(GameState::Playing)),
|
.run_if(in_state(GameState::Playing)),
|
||||||
@@ -64,6 +66,35 @@ pub fn plugin(app: &mut App) {
|
|||||||
app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
|
app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, Default, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
pub enum ViewMode {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
FreeMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewMode {
|
||||||
|
pub fn from_input(button: bool) -> Self {
|
||||||
|
if button {
|
||||||
|
Self::FreeMode
|
||||||
|
} else {
|
||||||
|
Self::Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_input(&mut self, button: bool) {
|
||||||
|
let new = Self::from_input(button);
|
||||||
|
*self = match (*self, new) {
|
||||||
|
(Self::FreeMode, _) | (_, Self::FreeMode) => Self::FreeMode,
|
||||||
|
_ => Self::Default,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_free(&self) -> bool {
|
||||||
|
matches!(self, Self::FreeMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The continuous inputs of a client for a tick. The instant inputs are sent via messages like `BackpackTogglePressed`.
|
/// The continuous inputs of a client for a tick. The instant inputs are sent via messages like `BackpackTogglePressed`.
|
||||||
#[derive(Component, Clone, Copy, Debug, Serialize, Deserialize, Reflect)]
|
#[derive(Component, Clone, Copy, Debug, Serialize, Deserialize, Reflect)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
@@ -75,7 +106,7 @@ pub struct Inputs {
|
|||||||
pub look_dir: Vec3,
|
pub look_dir: Vec3,
|
||||||
pub jump: bool,
|
pub jump: bool,
|
||||||
/// Determines if the camera can rotate freely around the player
|
/// Determines if the camera can rotate freely around the player
|
||||||
pub view_mode: bool,
|
pub view_mode: ViewMode,
|
||||||
pub trigger: bool,
|
pub trigger: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,23 +185,24 @@ fn collect_player_inputs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn head_change(
|
fn head_change(
|
||||||
//TODO: needs a 'LocalPlayer' at some point for multiplayer
|
mut commands: Commands,
|
||||||
query: Query<(Entity, &ActiveHead), (Changed<ActiveHead>, With<Player>)>,
|
query: Query<(Entity, &ActiveHead, &SelectedController), (Changed<ActiveHead>, With<Player>)>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
mut selected_controller: ResMut<SelectedController>,
|
|
||||||
mut event_controller_switch: MessageWriter<ControllerSwitchEvent>,
|
mut event_controller_switch: MessageWriter<ControllerSwitchEvent>,
|
||||||
) {
|
) {
|
||||||
for (entity, head) in query.iter() {
|
for (entity, head, selected_controller) in query.iter() {
|
||||||
let stats = heads_db.head_stats(head.0);
|
let stats = heads_db.head_stats(head.0);
|
||||||
let controller = match stats.controls {
|
let controller = match stats.controls {
|
||||||
HeadControls::Plane => SelectedController::Flying,
|
HeadControls::Plane => SelectedController::Flying,
|
||||||
HeadControls::Walk => SelectedController::Running,
|
HeadControls::Walk => SelectedController::Running,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!("player head changed: {} ({:?})", head.0, controller);
|
||||||
|
|
||||||
if *selected_controller != controller {
|
if *selected_controller != controller {
|
||||||
event_controller_switch.write(ControllerSwitchEvent { controller: entity });
|
event_controller_switch.write(ControllerSwitchEvent { controller: entity });
|
||||||
|
|
||||||
*selected_controller = controller;
|
commands.entity(entity).insert(controller);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use super::{ActiveHeads, HEAD_SLOTS};
|
|||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::heads::HeadsImages;
|
use crate::heads::HeadsImages;
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, backpack::UiHeadState, loading_assets::UIAssets, player::Player, protocol::is_server,
|
GameState, backpack::UiHeadState, loading_assets::UIAssets, player::LocalPlayer,
|
||||||
|
protocol::is_server,
|
||||||
};
|
};
|
||||||
use bevy::{ecs::spawn::SpawnIter, prelude::*};
|
use bevy::{ecs::spawn::SpawnIter, prelude::*};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -239,14 +240,10 @@ fn update_health(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sync(
|
fn sync(
|
||||||
active_heads: Query<Ref<ActiveHeads>, With<Player>>,
|
active_heads: Single<Ref<ActiveHeads>, With<LocalPlayer>>,
|
||||||
mut state: Single<&mut UiActiveHeads>,
|
mut state: Single<&mut UiActiveHeads>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
let Ok(active_heads) = active_heads.single() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if active_heads.is_changed() || active_heads.reloading() {
|
if active_heads.is_changed() || active_heads.reloading() {
|
||||||
state.selected_slot = active_heads.selected_slot;
|
state.selected_slot = active_heads.selected_slot;
|
||||||
|
|
||||||
|
|||||||
@@ -177,8 +177,11 @@ impl ActiveHeads {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)]
|
#[derive(EntityEvent)]
|
||||||
pub struct HeadChanged(pub usize);
|
pub struct HeadChanged {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub head: usize,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins(heads_ui::plugin);
|
app.add_plugins(heads_ui::plugin);
|
||||||
@@ -217,11 +220,10 @@ fn sync_hp(mut query: Query<(&mut ActiveHeads, &Hitpoints)>) {
|
|||||||
|
|
||||||
fn reload(
|
fn reload(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut active: Query<&mut ActiveHeads>,
|
mut active: Query<(&mut ActiveHeads, &mut AnimationFlags), With<Player>>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut flags: Single<&mut AnimationFlags, With<Player>>,
|
|
||||||
) {
|
) {
|
||||||
for mut active in active.iter_mut() {
|
for (mut active, mut flags) in active.iter_mut() {
|
||||||
if !active.reloading() {
|
if !active.reloading() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -249,6 +251,7 @@ fn reload(
|
|||||||
fn on_select_active_head(
|
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>>,
|
||||||
|
// TODO: unify into just one message
|
||||||
mut select_lefts: MessageReader<FromClient<SelectLeftPressed>>,
|
mut select_lefts: MessageReader<FromClient<SelectLeftPressed>>,
|
||||||
mut select_rights: MessageReader<FromClient<SelectRightPressed>>,
|
mut select_rights: MessageReader<FromClient<SelectRightPressed>>,
|
||||||
controllers: ClientToController,
|
controllers: ClientToController,
|
||||||
@@ -270,9 +273,10 @@ fn on_select_active_head(
|
|||||||
active_heads.current_slot = active_heads.selected_slot;
|
active_heads.current_slot = active_heads.selected_slot;
|
||||||
hp.set_health(active_heads.current().unwrap().health);
|
hp.set_health(active_heads.current().unwrap().health);
|
||||||
|
|
||||||
commands.trigger(HeadChanged(
|
commands.trigger(HeadChanged {
|
||||||
active_heads.heads[active_heads.current_slot].unwrap().head,
|
entity: player,
|
||||||
));
|
head: active_heads.heads[active_heads.current_slot].unwrap().head,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,9 +297,10 @@ fn on_select_active_head(
|
|||||||
active_heads.current_slot = active_heads.selected_slot;
|
active_heads.current_slot = active_heads.selected_slot;
|
||||||
hp.set_health(active_heads.current().unwrap().health);
|
hp.set_health(active_heads.current().unwrap().health);
|
||||||
|
|
||||||
commands.trigger(HeadChanged(
|
commands.trigger(HeadChanged {
|
||||||
active_heads.heads[active_heads.current_slot].unwrap().head,
|
entity: player,
|
||||||
));
|
head: active_heads.heads[active_heads.current_slot].unwrap().head,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,11 +308,11 @@ fn on_select_active_head(
|
|||||||
fn on_swap_backpack(
|
fn on_swap_backpack(
|
||||||
trigger: On<FromClient<BackpackSwapEvent>>,
|
trigger: On<FromClient<BackpackSwapEvent>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(&mut ActiveHeads, &mut Hitpoints, &mut Backpack), With<Player>>,
|
mut query: Query<(Entity, &mut ActiveHeads, &mut Hitpoints, &mut Backpack), With<Player>>,
|
||||||
) {
|
) {
|
||||||
let backpack_slot = trigger.event().0;
|
let backpack_slot = trigger.event().0;
|
||||||
|
|
||||||
let Ok((mut active_heads, mut hp, mut backpack)) = query.single_mut() else {
|
let Ok((player, mut active_heads, mut hp, mut backpack)) = query.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -326,7 +331,8 @@ fn on_swap_backpack(
|
|||||||
|
|
||||||
hp.set_health(active_heads.current().unwrap().health);
|
hp.set_health(active_heads.current().unwrap().health);
|
||||||
|
|
||||||
commands.trigger(HeadChanged(
|
commands.trigger(HeadChanged {
|
||||||
active_heads.heads[active_heads.selected_slot].unwrap().head,
|
entity: player,
|
||||||
));
|
head: active_heads.heads[active_heads.selected_slot].unwrap().head,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,9 +132,16 @@ pub fn spawn(
|
|||||||
fn on_kill(
|
fn on_kill(
|
||||||
trigger: On<Kill>,
|
trigger: On<Kill>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(&Transform, &ActiveHead, &mut ActiveHeads, &mut Hitpoints)>,
|
mut query: Query<(
|
||||||
|
Entity,
|
||||||
|
&Transform,
|
||||||
|
&ActiveHead,
|
||||||
|
&mut ActiveHeads,
|
||||||
|
&mut Hitpoints,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
let Ok((transform, active, mut heads, mut hp)) = query.get_mut(trigger.event().entity) else {
|
let Ok((player, transform, active, mut heads, mut hp)) = query.get_mut(trigger.event().entity)
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -143,31 +150,51 @@ fn on_kill(
|
|||||||
if let Some(new_head) = heads.loose_current() {
|
if let Some(new_head) = heads.loose_current() {
|
||||||
hp.set_health(heads.current().unwrap().health);
|
hp.set_health(heads.current().unwrap().health);
|
||||||
|
|
||||||
commands.trigger(HeadChanged(new_head));
|
commands.trigger(HeadChanged {
|
||||||
|
entity: player,
|
||||||
|
head: new_head,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_update_head_mesh(
|
fn on_update_head_mesh(
|
||||||
trigger: On<HeadChanged>,
|
trigger: On<HeadChanged>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mesh_children: Single<&Children, With<PlayerBodyMesh>>,
|
player_id: Query<&PlayerId, With<Player>>,
|
||||||
|
children: Query<&Children>,
|
||||||
|
player_body_mesh: Query<&PlayerBodyMesh>,
|
||||||
animated_characters: Query<&AnimatedCharacter>,
|
animated_characters: Query<&AnimatedCharacter>,
|
||||||
mut player: Single<&mut ActiveHead, With<Player>>,
|
mut active_head: Query<&mut ActiveHead>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let animated_char = mesh_children
|
let player_id = player_id.get(trigger.entity)?.clone();
|
||||||
.iter()
|
|
||||||
.find(|child| animated_characters.contains(*child))
|
|
||||||
.ok_or("tried to update head mesh before AnimatedCharacter was readded")?;
|
|
||||||
|
|
||||||
player.0 = trigger.0;
|
let player_body_mesh = children
|
||||||
|
.get(trigger.entity)?
|
||||||
|
.iter()
|
||||||
|
.find(|child| player_body_mesh.get(*child).is_ok())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let animated_character = children
|
||||||
|
.get(player_body_mesh)?
|
||||||
|
.iter()
|
||||||
|
.find(|child| animated_characters.get(*child).is_ok())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut active_head = active_head.get_mut(trigger.entity)?;
|
||||||
|
active_head.0 = trigger.head;
|
||||||
|
}
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.entity(animated_char)
|
.entity(animated_character)
|
||||||
.insert(AnimatedCharacter::new(trigger.0));
|
.insert(AnimatedCharacter::new(trigger.head));
|
||||||
|
|
||||||
commands.server_trigger(ToClients {
|
commands.server_trigger(ToClients {
|
||||||
mode: SendMode::Broadcast,
|
mode: SendMode::Broadcast,
|
||||||
message: ClientHeadChanged(trigger.0 as u64),
|
message: ClientHeadChanged {
|
||||||
|
player: player_id,
|
||||||
|
head: trigger.head,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::protocol::PlayerId;
|
||||||
|
|
||||||
|
// TODO: remove in favour of client side change detection
|
||||||
#[derive(Clone, Event, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Event, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct ClientHeadChanged(pub u64);
|
pub struct ClientHeadChanged {
|
||||||
|
pub player: PlayerId,
|
||||||
|
pub head: usize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Event, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Event, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum PlaySound {
|
pub enum PlaySound {
|
||||||
|
|||||||
Reference in New Issue
Block a user