fixes head switching (#96)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use crate::GameState;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::control::Inputs;
|
||||
use crate::control::ViewMode;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::physics_layers::GameLayer;
|
||||
#[cfg(feature = "client")]
|
||||
@@ -31,7 +32,7 @@ pub struct CameraRotationInput(pub Vec2);
|
||||
#[reflect(Resource)]
|
||||
pub struct CameraState {
|
||||
pub cutscene: bool,
|
||||
pub look_around: bool,
|
||||
pub view_mode: ViewMode,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Debug, Default)]
|
||||
@@ -85,10 +86,10 @@ fn update_look_around(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
) {
|
||||
let look_around = inputs.view_mode;
|
||||
let view_mode = inputs.view_mode;
|
||||
|
||||
if look_around != cam_state.look_around {
|
||||
cam_state.look_around = look_around;
|
||||
if view_mode != cam_state.view_mode {
|
||||
cam_state.view_mode = view_mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,9 +101,9 @@ fn update_ui(
|
||||
query: Query<Entity, With<CameraUi>>,
|
||||
) {
|
||||
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((
|
||||
CameraUi,
|
||||
Node {
|
||||
@@ -194,7 +195,7 @@ fn rotate_view(
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut cam: Single<&mut CameraRotationInput>,
|
||||
) {
|
||||
if !inputs.view_mode {
|
||||
if !inputs.view_mode.is_free() {
|
||||
cam.x = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
control::{ControllerSet, Inputs, LookDirMovement},
|
||||
control::{ControllerSet, Inputs, LookDirMovement, SelectedController},
|
||||
player::{LocalPlayer, PlayerBodyMesh},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
@@ -19,14 +19,20 @@ pub fn plugin(app: &mut App) {
|
||||
fn rotate_rig(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
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>>,
|
||||
) {
|
||||
if inputs.view_mode {
|
||||
if inputs.view_mode.is_free() {
|
||||
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) {
|
||||
let look_dir = look_dir.0;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
client::control::CharacterInputEnabled,
|
||||
control::{
|
||||
BackpackButtonPress, CashHealPressed, ClientInputs, ControllerSet, Inputs, LocalInputs,
|
||||
LookDirMovement, SelectLeftPressed, SelectRightPressed,
|
||||
LookDirMovement, SelectLeftPressed, SelectRightPressed, ViewMode,
|
||||
},
|
||||
player::{LocalPlayer, PlayerBodyMesh},
|
||||
};
|
||||
@@ -146,8 +146,11 @@ fn gamepad_controls(
|
||||
|
||||
inputs.0.move_dir += move_dir.clamp_length_max(1.0);
|
||||
inputs.0.jump |= gamepad.pressed(GamepadButton::South);
|
||||
inputs.0.view_mode |= gamepad.pressed(GamepadButton::LeftTrigger2);
|
||||
inputs.0.trigger |= gamepad.pressed(GamepadButton::RightTrigger2);
|
||||
inputs
|
||||
.0
|
||||
.view_mode
|
||||
.merge_input(gamepad.pressed(GamepadButton::LeftTrigger2));
|
||||
|
||||
if gamepad.just_pressed(GamepadButton::DPadUp) {
|
||||
backpack_inputs.write(BackpackButtonPress::Toggle);
|
||||
@@ -212,8 +215,8 @@ fn keyboard_controls(
|
||||
|
||||
inputs.0.move_dir = direction;
|
||||
inputs.0.jump = keyboard.pressed(KeyCode::Space);
|
||||
inputs.0.view_mode = keyboard.pressed(KeyCode::Tab);
|
||||
inputs.0.trigger = mouse.pressed(MouseButton::Left);
|
||||
inputs.0.view_mode = ViewMode::from_input(keyboard.pressed(KeyCode::Tab));
|
||||
|
||||
if keyboard.just_pressed(KeyCode::KeyB) {
|
||||
backpack_inputs.write(BackpackButtonPress::Toggle);
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
global_observer,
|
||||
heads_database::{HeadControls, HeadsDatabase},
|
||||
loading_assets::AudioAssets,
|
||||
player::{LocalPlayer, PlayerBodyMesh},
|
||||
player::{LocalPlayer, Player, PlayerBodyMesh},
|
||||
protocol::{ClientHeadChanged, PlaySound, PlayerId, messages::AssignClientPlayer},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
@@ -59,23 +59,38 @@ pub enum PlayerAssignmentState {
|
||||
Confirmed,
|
||||
}
|
||||
|
||||
// TODO: currently a networked message.
|
||||
// can be done by just using local change detection on `ActiveHead`?
|
||||
fn on_client_update_head_mesh(
|
||||
trigger: On<ClientHeadChanged>,
|
||||
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>,
|
||||
audio_assets: Res<AudioAssets>,
|
||||
sfx: Query<&AudioPlayer>,
|
||||
) -> Result {
|
||||
let head = trigger.0 as usize;
|
||||
let (body_mesh, mesh_children) = *body_mesh;
|
||||
let (player_children, _) = player
|
||||
.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);
|
||||
|
||||
commands.trigger(PlaySound::Head(head_str.to_string()));
|
||||
|
||||
//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();
|
||||
}
|
||||
if head_db.head_stats(head).controls == HeadControls::Plane {
|
||||
|
||||
@@ -72,12 +72,22 @@ fn set_animation_flags(
|
||||
pub fn reset_upon_switch(
|
||||
mut c: Commands,
|
||||
mut event_controller_switch: MessageReader<ControllerSwitchEvent>,
|
||||
selected_controller: Res<SelectedController>,
|
||||
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() {
|
||||
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;
|
||||
|
||||
@@ -165,6 +175,7 @@ impl Default for MovementSpeedFactor {
|
||||
MoveInput,
|
||||
MovementSpeedFactor,
|
||||
TransformInterpolation,
|
||||
SelectedController::Running,
|
||||
CharacterMovement = RUNNING_MOVEMENT_CONFIG.movement,
|
||||
ControllerSettings = RUNNING_MOVEMENT_CONFIG.settings,
|
||||
CharacterGravity = RUNNING_MOVEMENT_CONFIG.gravity,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::ControllerSet;
|
||||
use crate::{
|
||||
GameState,
|
||||
control::{Inputs, controller_common::MovementSpeedFactor},
|
||||
control::{Inputs, SelectedController, controller_common::MovementSpeedFactor},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use happy_feet::prelude::MoveInput;
|
||||
@@ -19,8 +19,17 @@ impl Plugin for CharacterControllerPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_controls(mut query: Query<(&mut MoveInput, &MovementSpeedFactor, &Inputs)>) {
|
||||
for (mut char_input, factor, inputs) in query.iter_mut() {
|
||||
char_input.set(inputs.look_dir * factor.0);
|
||||
pub fn apply_controls(
|
||||
mut query: Query<(
|
||||
&mut MoveInput,
|
||||
&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::{
|
||||
GameState,
|
||||
animation::AnimationFlags,
|
||||
control::{ControllerSet, ControllerSettings, Inputs, controller_common::MovementSpeedFactor},
|
||||
control::{
|
||||
ControllerSet, ControllerSettings, Inputs, SelectedController,
|
||||
controller_common::MovementSpeedFactor,
|
||||
},
|
||||
protocol::is_server,
|
||||
};
|
||||
#[cfg(feature = "client")]
|
||||
@@ -41,7 +44,7 @@ fn rotate_view(
|
||||
) {
|
||||
let (inputs, children) = controller.into_inner();
|
||||
|
||||
if inputs.view_mode {
|
||||
if inputs.view_mode.is_free() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,11 +67,24 @@ fn apply_controls(
|
||||
&ControllerSettings,
|
||||
&MovementSpeedFactor,
|
||||
&Inputs,
|
||||
&SelectedController,
|
||||
)>,
|
||||
) {
|
||||
for (mut move_input, mut grounding, mut velocity, mut flags, settings, move_factor, inputs) in
|
||||
query.iter_mut()
|
||||
for (
|
||||
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 mut direction = inputs.move_dir.extend(0.0).xzy();
|
||||
|
||||
@@ -20,7 +20,8 @@ pub enum ControllerSet {
|
||||
ApplyControlsRun,
|
||||
}
|
||||
|
||||
#[derive(Resource, Debug, Clone, Copy, PartialEq, Default)]
|
||||
#[derive(Component, Reflect, Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[reflect(Component)]
|
||||
pub enum SelectedController {
|
||||
Flying,
|
||||
#[default]
|
||||
@@ -35,8 +36,9 @@ pub fn plugin(app: &mut App) {
|
||||
#[cfg(feature = "client")]
|
||||
app.register_type::<LocalInputs>();
|
||||
|
||||
app.register_type::<SelectedController>();
|
||||
|
||||
app.init_resource::<LookDirMovement>();
|
||||
app.init_resource::<SelectedController>();
|
||||
|
||||
app.add_message::<ControllerSwitchEvent>()
|
||||
.add_message::<BackpackButtonPress>();
|
||||
@@ -48,8 +50,8 @@ pub fn plugin(app: &mut App) {
|
||||
app.configure_sets(
|
||||
FixedUpdate,
|
||||
(
|
||||
ControllerSet::ApplyControlsFly.run_if(resource_equals(SelectedController::Flying)),
|
||||
ControllerSet::ApplyControlsRun.run_if(resource_equals(SelectedController::Running)),
|
||||
ControllerSet::ApplyControlsFly,
|
||||
ControllerSet::ApplyControlsRun,
|
||||
)
|
||||
.chain()
|
||||
.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)));
|
||||
}
|
||||
|
||||
#[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`.
|
||||
#[derive(Component, Clone, Copy, Debug, Serialize, Deserialize, Reflect)]
|
||||
#[reflect(Component, Default)]
|
||||
@@ -75,7 +106,7 @@ pub struct Inputs {
|
||||
pub look_dir: Vec3,
|
||||
pub jump: bool,
|
||||
/// Determines if the camera can rotate freely around the player
|
||||
pub view_mode: bool,
|
||||
pub view_mode: ViewMode,
|
||||
pub trigger: bool,
|
||||
}
|
||||
|
||||
@@ -154,23 +185,24 @@ fn collect_player_inputs(
|
||||
}
|
||||
|
||||
fn head_change(
|
||||
//TODO: needs a 'LocalPlayer' at some point for multiplayer
|
||||
query: Query<(Entity, &ActiveHead), (Changed<ActiveHead>, With<Player>)>,
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &ActiveHead, &SelectedController), (Changed<ActiveHead>, With<Player>)>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
mut selected_controller: ResMut<SelectedController>,
|
||||
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 controller = match stats.controls {
|
||||
HeadControls::Plane => SelectedController::Flying,
|
||||
HeadControls::Walk => SelectedController::Running,
|
||||
};
|
||||
|
||||
info!("player head changed: {} ({:?})", head.0, controller);
|
||||
|
||||
if *selected_controller != controller {
|
||||
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")]
|
||||
use crate::heads::HeadsImages;
|
||||
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 serde::{Deserialize, Serialize};
|
||||
@@ -239,14 +240,10 @@ fn update_health(
|
||||
}
|
||||
|
||||
fn sync(
|
||||
active_heads: Query<Ref<ActiveHeads>, With<Player>>,
|
||||
active_heads: Single<Ref<ActiveHeads>, With<LocalPlayer>>,
|
||||
mut state: Single<&mut UiActiveHeads>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let Ok(active_heads) = active_heads.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if active_heads.is_changed() || active_heads.reloading() {
|
||||
state.selected_slot = active_heads.selected_slot;
|
||||
|
||||
|
||||
@@ -177,8 +177,11 @@ impl ActiveHeads {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct HeadChanged(pub usize);
|
||||
#[derive(EntityEvent)]
|
||||
pub struct HeadChanged {
|
||||
pub entity: Entity,
|
||||
pub head: usize,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(heads_ui::plugin);
|
||||
@@ -217,11 +220,10 @@ fn sync_hp(mut query: Query<(&mut ActiveHeads, &Hitpoints)>) {
|
||||
|
||||
fn reload(
|
||||
mut commands: Commands,
|
||||
mut active: Query<&mut ActiveHeads>,
|
||||
mut active: Query<(&mut ActiveHeads, &mut AnimationFlags), With<Player>>,
|
||||
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() {
|
||||
continue;
|
||||
}
|
||||
@@ -249,6 +251,7 @@ fn reload(
|
||||
fn on_select_active_head(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(&mut ActiveHeads, &mut Hitpoints), With<Player>>,
|
||||
// TODO: unify into just one message
|
||||
mut select_lefts: MessageReader<FromClient<SelectLeftPressed>>,
|
||||
mut select_rights: MessageReader<FromClient<SelectRightPressed>>,
|
||||
controllers: ClientToController,
|
||||
@@ -270,9 +273,10 @@ fn on_select_active_head(
|
||||
active_heads.current_slot = active_heads.selected_slot;
|
||||
hp.set_health(active_heads.current().unwrap().health);
|
||||
|
||||
commands.trigger(HeadChanged(
|
||||
active_heads.heads[active_heads.current_slot].unwrap().head,
|
||||
));
|
||||
commands.trigger(HeadChanged {
|
||||
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;
|
||||
hp.set_health(active_heads.current().unwrap().health);
|
||||
|
||||
commands.trigger(HeadChanged(
|
||||
active_heads.heads[active_heads.current_slot].unwrap().head,
|
||||
));
|
||||
commands.trigger(HeadChanged {
|
||||
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(
|
||||
trigger: On<FromClient<BackpackSwapEvent>>,
|
||||
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 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;
|
||||
};
|
||||
|
||||
@@ -326,7 +331,8 @@ fn on_swap_backpack(
|
||||
|
||||
hp.set_health(active_heads.current().unwrap().health);
|
||||
|
||||
commands.trigger(HeadChanged(
|
||||
active_heads.heads[active_heads.selected_slot].unwrap().head,
|
||||
));
|
||||
commands.trigger(HeadChanged {
|
||||
entity: player,
|
||||
head: active_heads.heads[active_heads.selected_slot].unwrap().head,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,9 +132,16 @@ pub fn spawn(
|
||||
fn on_kill(
|
||||
trigger: On<Kill>,
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -143,31 +150,51 @@ fn on_kill(
|
||||
if let Some(new_head) = heads.loose_current() {
|
||||
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(
|
||||
trigger: On<HeadChanged>,
|
||||
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>,
|
||||
mut player: Single<&mut ActiveHead, With<Player>>,
|
||||
mut active_head: Query<&mut ActiveHead>,
|
||||
) -> Result {
|
||||
let animated_char = mesh_children
|
||||
.iter()
|
||||
.find(|child| animated_characters.contains(*child))
|
||||
.ok_or("tried to update head mesh before AnimatedCharacter was readded")?;
|
||||
let player_id = player_id.get(trigger.entity)?.clone();
|
||||
|
||||
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
|
||||
.entity(animated_char)
|
||||
.insert(AnimatedCharacter::new(trigger.0));
|
||||
.entity(animated_character)
|
||||
.insert(AnimatedCharacter::new(trigger.head));
|
||||
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: ClientHeadChanged(trigger.0 as u64),
|
||||
message: ClientHeadChanged {
|
||||
player: player_id,
|
||||
head: trigger.head,
|
||||
},
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::protocol::PlayerId;
|
||||
|
||||
// TODO: remove in favour of client side change detection
|
||||
#[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)]
|
||||
pub enum PlaySound {
|
||||
|
||||
Reference in New Issue
Block a user