Simpler + Better Inputs (#82)
* switch to events for instantaneous inputs * input simplification + improvements * fix trail crash * fix clippy warnings * qualify `Trail` in fn signature
This commit is contained in:
@@ -14,18 +14,10 @@ use crate::{
|
||||
};
|
||||
#[cfg(feature = "server")]
|
||||
use crate::{
|
||||
aim::AimTarget,
|
||||
character::CharacterHierarchy,
|
||||
control::{ControlState, Inputs},
|
||||
head::ActiveHead,
|
||||
heads::ActiveHeads,
|
||||
heads_database::HeadsDatabase,
|
||||
player::Player,
|
||||
protocol::ClientToController,
|
||||
aim::AimTarget, character::CharacterHierarchy, control::Inputs, head::ActiveHead,
|
||||
heads::ActiveHeads, heads_database::HeadsDatabase, player::Player,
|
||||
};
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
#[cfg(feature = "server")]
|
||||
use bevy_replicon::prelude::FromClient;
|
||||
use bevy_replicon::prelude::{ClientState, SendMode, ServerTriggerExt, ToClients};
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
pub use healing::Healing;
|
||||
@@ -194,20 +186,10 @@ fn explode_projectiles(mut commands: Commands, query: Query<(Entity, &ExplodingP
|
||||
#[cfg(feature = "server")]
|
||||
fn on_trigger_state(
|
||||
mut res: ResMut<TriggerStateRes>,
|
||||
players: Query<&ActiveHead, With<Player>>,
|
||||
clients: ClientToController,
|
||||
mut controls: MessageReader<FromClient<ControlState>>,
|
||||
headdb: Res<HeadsDatabase>,
|
||||
time: Res<Time>,
|
||||
players: Query<(&ActiveHead, &Inputs), With<Player>>,
|
||||
) {
|
||||
for controls in controls.read() {
|
||||
let player = clients.get_controller(controls.client_id);
|
||||
let player_head = players.get(player).unwrap();
|
||||
res.active = controls.trigger;
|
||||
if controls.just_triggered {
|
||||
let head_stats = headdb.head_stats(player_head.0);
|
||||
res.next_trigger_timestamp = time.elapsed_secs() + head_stats.shoot_offset;
|
||||
}
|
||||
for (_, inputs) in players.iter() {
|
||||
res.active = inputs.trigger;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::GameState;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::control::Inputs;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::physics_layers::GameLayer;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::{
|
||||
control::{ControlState, LookDirMovement},
|
||||
loading_assets::UIAssets,
|
||||
};
|
||||
use crate::player::LocalPlayer;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::{control::LookDirMovement, loading_assets::UIAssets};
|
||||
#[cfg(feature = "client")]
|
||||
use avian3d::prelude::SpatialQuery;
|
||||
#[cfg(feature = "client")]
|
||||
@@ -80,8 +81,11 @@ fn startup(mut commands: Commands) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_look_around(controls: Res<ControlState>, mut cam_state: ResMut<CameraState>) {
|
||||
let look_around = controls.view_mode;
|
||||
fn update_look_around(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
) {
|
||||
let look_around = inputs.view_mode;
|
||||
|
||||
if look_around != cam_state.look_around {
|
||||
cam_state.look_around = look_around;
|
||||
@@ -176,11 +180,11 @@ fn update(
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn rotate_view(
|
||||
controls: Res<ControlState>,
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut cam: Single<&mut CameraRotationInput>,
|
||||
) {
|
||||
if !controls.view_mode {
|
||||
if !inputs.view_mode {
|
||||
cam.x = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub struct Cash;
|
||||
struct CashText;
|
||||
|
||||
#[derive(Component, Reflect, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CashResource {
|
||||
pub struct CashInventory {
|
||||
pub cash: i32,
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn plugin(app: &mut App) {
|
||||
fn on_cash_collect(
|
||||
_trigger: On<CashCollectEvent>,
|
||||
mut commands: Commands,
|
||||
mut cash: Single<&mut CashResource>,
|
||||
mut cash: Single<&mut CashInventory>,
|
||||
) {
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, ToClients};
|
||||
|
||||
@@ -58,7 +58,7 @@ fn rotate(time: Res<Time>, mut query: Query<&mut Rotation, With<Cash>>) {
|
||||
}
|
||||
|
||||
fn update_ui(
|
||||
cash: Single<&CashResource, Changed<CashResource>>,
|
||||
cash: Single<&CashInventory, Changed<CashInventory>>,
|
||||
text: Query<Entity, With<CashText>>,
|
||||
mut writer: TextUiWriter,
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::{
|
||||
cash::CashResource, control::ControlState, hitpoints::Hitpoints, player::Player,
|
||||
protocol::PlaySound,
|
||||
cash::CashInventory,
|
||||
control::CashHealPressed,
|
||||
hitpoints::Hitpoints,
|
||||
player::Player,
|
||||
protocol::{ClientToController, PlaySound},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_replicon::prelude::{FromClient, SendMode, ServerTriggerExt, ToClients};
|
||||
@@ -17,32 +20,29 @@ struct HealAction {
|
||||
|
||||
fn on_heal_trigger(
|
||||
mut commands: Commands,
|
||||
mut cash: Single<&mut CashResource>,
|
||||
mut query: Query<&mut Hitpoints, With<Player>>,
|
||||
mut controls: MessageReader<FromClient<ControlState>>,
|
||||
controllers: ClientToController,
|
||||
mut query: Query<(&mut Hitpoints, &mut CashInventory), With<Player>>,
|
||||
mut inputs: MessageReader<FromClient<CashHealPressed>>,
|
||||
) {
|
||||
for controls in controls.read() {
|
||||
for mut hp in query.iter_mut() {
|
||||
if !controls.cash_heal {
|
||||
continue;
|
||||
}
|
||||
for press in inputs.read() {
|
||||
let controller = controllers.get_controller(press.client_id);
|
||||
let (mut hp, mut cash) = query.get_mut(controller).unwrap();
|
||||
|
||||
if hp.max() || cash.cash == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let action = heal(cash.cash, hp.get().1 - hp.get().0);
|
||||
|
||||
hp.heal(action.damage_healed);
|
||||
|
||||
cash.cash = cash.cash.saturating_sub(action.cost);
|
||||
|
||||
//TODO: trigger ui cost animation
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: PlaySound::CashHeal,
|
||||
});
|
||||
if hp.max() || cash.cash == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let action = heal(cash.cash, hp.get().1 - hp.get().0);
|
||||
|
||||
hp.heal(action.damage_healed);
|
||||
|
||||
cash.cash = cash.cash.saturating_sub(action.cost);
|
||||
|
||||
//TODO: trigger ui cost animation
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: PlaySound::CashHeal,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ fn setup_once_loaded(
|
||||
#[cfg(feature = "dbg")]
|
||||
fn debug_show_projectile_origin_and_trial(
|
||||
mut gizmos: Gizmos,
|
||||
query: Query<&GlobalTransform, Or<(With<ProjectileOrigin>, With<Trail>)>>,
|
||||
query: Query<&GlobalTransform, Or<(With<ProjectileOrigin>, With<crate::utils::trail::Trail>)>>,
|
||||
) {
|
||||
for projectile_origin in query.iter() {
|
||||
gizmos.sphere(
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::{
|
||||
};
|
||||
#[cfg(feature = "client")]
|
||||
use crate::{
|
||||
control::{ControlState, LookDirMovement},
|
||||
player::PlayerBodyMesh,
|
||||
control::LookDirMovement,
|
||||
player::{LocalPlayer, PlayerBodyMesh},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_replicon::prelude::ClientState;
|
||||
@@ -36,17 +36,24 @@ impl Plugin for CharacterControllerPlugin {
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn rotate_view(
|
||||
controls: Res<ControlState>,
|
||||
controller: Single<(&Inputs, &Children), With<LocalPlayer>>,
|
||||
mut player_mesh: Query<&mut Transform, With<PlayerBodyMesh>>,
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut player: Query<&mut Transform, With<PlayerBodyMesh>>,
|
||||
) {
|
||||
for mut tr in player.iter_mut() {
|
||||
if controls.view_mode {
|
||||
continue;
|
||||
}
|
||||
let (inputs, children) = controller.into_inner();
|
||||
|
||||
tr.rotate_y(look_dir.0.x * -0.001);
|
||||
if inputs.view_mode {
|
||||
return;
|
||||
}
|
||||
|
||||
children.iter().find(|&child| {
|
||||
if let Ok(mut body_transform) = player_mesh.get_mut(child) {
|
||||
body_transform.rotate_y(look_dir.0.x * -0.001);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn apply_controls(
|
||||
|
||||
@@ -2,8 +2,8 @@ use crate::{
|
||||
GameState,
|
||||
head::ActiveHead,
|
||||
heads_database::{HeadControls, HeadsDatabase},
|
||||
player::{LocalPlayerId, Player},
|
||||
protocol::{ClientToController, PlayerIdMap},
|
||||
player::Player,
|
||||
protocol::ClientToController,
|
||||
};
|
||||
use bevy::{ecs::entity::MapEntities, prelude::*};
|
||||
use bevy_replicon::{
|
||||
@@ -28,11 +28,13 @@ pub enum ControllerSet {
|
||||
pub struct SelectedController(pub ControllerSet);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<ControllerSettings>();
|
||||
app.register_type::<LookDirMovement>();
|
||||
app.register_type::<Inputs>();
|
||||
app.register_type::<ControllerSettings>()
|
||||
.register_type::<LookDirMovement>()
|
||||
.register_type::<Inputs>();
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
app.register_type::<LocalInputs>();
|
||||
|
||||
app.init_resource::<ControlState>();
|
||||
app.init_resource::<LookDirMovement>();
|
||||
app.init_resource::<SelectedController>();
|
||||
|
||||
@@ -58,22 +60,17 @@ pub fn plugin(app: &mut App) {
|
||||
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
(
|
||||
collect_player_inputs.run_if(in_state(ClientState::Disconnected)),
|
||||
update_local_player_inputs
|
||||
.run_if(resource_exists::<LocalPlayerId>)
|
||||
.run_if(in_state(ClientState::Connected)),
|
||||
)
|
||||
.chain()
|
||||
.run_if(in_state(GameState::Playing))
|
||||
collect_player_inputs
|
||||
.run_if(in_state(ClientState::Disconnected).and(in_state(GameState::Playing)))
|
||||
.after(ClientSystems::Receive),
|
||||
);
|
||||
app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
|
||||
}
|
||||
|
||||
// TODO: Split this into an enum of individual input commands, e.g. `JustJumped`
|
||||
#[derive(Resource, Debug, Clone, Copy, Message, PartialEq, Serialize, Deserialize, Reflect)]
|
||||
pub struct ControlState {
|
||||
/// 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)]
|
||||
pub struct Inputs {
|
||||
/// Movement direction with a maximum length of 1.0
|
||||
pub move_dir: Vec2,
|
||||
/// The current direction that the character is facing
|
||||
@@ -83,17 +80,9 @@ pub struct ControlState {
|
||||
/// Determines if the camera can rotate freely around the player
|
||||
pub view_mode: bool,
|
||||
pub trigger: bool,
|
||||
pub just_triggered: bool,
|
||||
pub select_left: bool,
|
||||
pub select_right: bool,
|
||||
pub backpack_toggle: bool,
|
||||
pub backpack_swap: bool,
|
||||
pub backpack_left: bool,
|
||||
pub backpack_right: bool,
|
||||
pub cash_heal: bool,
|
||||
}
|
||||
|
||||
impl Default for ControlState {
|
||||
impl Default for Inputs {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
move_dir: Default::default(),
|
||||
@@ -101,25 +90,44 @@ impl Default for ControlState {
|
||||
jump: Default::default(),
|
||||
view_mode: Default::default(),
|
||||
trigger: Default::default(),
|
||||
just_triggered: Default::default(),
|
||||
select_left: Default::default(),
|
||||
select_right: Default::default(),
|
||||
backpack_toggle: Default::default(),
|
||||
backpack_swap: Default::default(),
|
||||
backpack_left: Default::default(),
|
||||
backpack_right: Default::default(),
|
||||
cash_heal: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for ControlState {
|
||||
impl MapEntities for Inputs {
|
||||
fn map_entities<E: EntityMapper>(&mut self, _entity_mapper: &mut E) {}
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Deref, DerefMut, Reflect, Serialize, Deserialize)]
|
||||
#[reflect(Component, Default)]
|
||||
pub struct Inputs(pub ControlState);
|
||||
/// A message to tell the server what inputs the client pressed this tick
|
||||
#[derive(Debug, Clone, Copy, Message, Serialize, Deserialize, Reflect)]
|
||||
pub struct ClientInputs(pub Inputs);
|
||||
|
||||
/// A cache to collect inputs into clientside, so that they don't get overwritten by replication from the server
|
||||
#[cfg(feature = "client")]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct LocalInputs(pub Inputs);
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct SelectLeftPressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct SelectRightPressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct BackpackTogglePressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct BackpackSwapPressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct BackpackLeftPressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct BackpackRightPressed;
|
||||
|
||||
#[derive(Message, Serialize, Deserialize)]
|
||||
pub struct CashHealPressed;
|
||||
|
||||
#[derive(Resource, Default, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
@@ -147,29 +155,16 @@ pub struct ControllerSwitchEvent {
|
||||
fn collect_player_inputs(
|
||||
mut players: Query<&mut Inputs>,
|
||||
clients: ClientToController,
|
||||
mut input_messages: MessageReader<FromClient<ControlState>>,
|
||||
mut input_messages: MessageReader<FromClient<ClientInputs>>,
|
||||
) {
|
||||
for msg in input_messages.read() {
|
||||
let player = clients.get_controller(msg.client_id);
|
||||
let mut inputs = players.get_mut(player).unwrap();
|
||||
|
||||
inputs.0 = msg.message;
|
||||
*inputs = msg.message.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Overwrite the input cache replicated to the local player with the actual current inputs
|
||||
fn update_local_player_inputs(
|
||||
mut players: Query<&mut Inputs>,
|
||||
player_ids: Res<PlayerIdMap>,
|
||||
local_id: Res<LocalPlayerId>,
|
||||
control_state: Res<ControlState>,
|
||||
) {
|
||||
let player = player_ids.get(&local_id.id).unwrap();
|
||||
let mut inputs = players.get_mut(*player).unwrap();
|
||||
|
||||
inputs.0 = *control_state;
|
||||
}
|
||||
|
||||
fn head_change(
|
||||
//TODO: needs a 'LocalPlayer' at some point for multiplayer
|
||||
query: Query<(Entity, &ActiveHead), (Changed<ActiveHead>, With<Player>)>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[cfg(feature = "server")]
|
||||
use crate::animation::AnimationFlags;
|
||||
use crate::protocol::PlaySound;
|
||||
use crate::{
|
||||
GameState,
|
||||
backpack::{BackbackSwapEvent, Backpack},
|
||||
@@ -9,7 +9,11 @@ use crate::{
|
||||
player::Player,
|
||||
};
|
||||
#[cfg(feature = "server")]
|
||||
use crate::{control::ControlState, protocol::PlaySound};
|
||||
use crate::{
|
||||
animation::AnimationFlags,
|
||||
control::{SelectLeftPressed, SelectRightPressed},
|
||||
protocol::ClientToController,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
#[cfg(feature = "server")]
|
||||
use bevy_replicon::prelude::FromClient;
|
||||
@@ -249,38 +253,53 @@ fn reload(
|
||||
fn on_select_active_head(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(&mut ActiveHeads, &mut Hitpoints), With<Player>>,
|
||||
mut controls: MessageReader<FromClient<ControlState>>,
|
||||
mut select_lefts: MessageReader<FromClient<SelectLeftPressed>>,
|
||||
mut select_rights: MessageReader<FromClient<SelectRightPressed>>,
|
||||
controllers: ClientToController,
|
||||
) {
|
||||
for controls in controls.read() {
|
||||
for (mut active_heads, mut hp) in query.iter_mut() {
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, ToClients};
|
||||
for press in select_lefts.read() {
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, ToClients};
|
||||
|
||||
if !controls.select_right && !controls.select_left {
|
||||
continue;
|
||||
}
|
||||
let player = controllers.get_controller(press.client_id);
|
||||
let (mut active_heads, mut hp) = query.get_mut(player).unwrap();
|
||||
|
||||
if controls.select_right {
|
||||
active_heads.selected_slot = (active_heads.selected_slot + 1) % HEAD_SLOTS;
|
||||
}
|
||||
active_heads.selected_slot = (active_heads.selected_slot + (HEAD_SLOTS - 1)) % HEAD_SLOTS;
|
||||
|
||||
if controls.select_left {
|
||||
active_heads.selected_slot =
|
||||
(active_heads.selected_slot + (HEAD_SLOTS - 1)) % HEAD_SLOTS;
|
||||
}
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: PlaySound::Selection,
|
||||
});
|
||||
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: PlaySound::Selection,
|
||||
});
|
||||
if active_heads.head(active_heads.selected_slot).is_some() {
|
||||
active_heads.current_slot = active_heads.selected_slot;
|
||||
hp.set_health(active_heads.current().unwrap().health);
|
||||
|
||||
if active_heads.head(active_heads.selected_slot).is_some() {
|
||||
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(
|
||||
active_heads.heads[active_heads.current_slot].unwrap().head,
|
||||
));
|
||||
}
|
||||
for press in select_rights.read() {
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, ToClients};
|
||||
|
||||
let player = controllers.get_controller(press.client_id);
|
||||
let (mut active_heads, mut hp) = query.get_mut(player).unwrap();
|
||||
|
||||
active_heads.selected_slot = (active_heads.selected_slot + 1) % HEAD_SLOTS;
|
||||
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: PlaySound::Selection,
|
||||
});
|
||||
|
||||
if active_heads.head(active_heads.selected_slot).is_some() {
|
||||
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,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "client")]
|
||||
use crate::control::LocalInputs;
|
||||
use crate::{
|
||||
GameState,
|
||||
cash::{Cash, CashCollectEvent},
|
||||
@@ -17,6 +19,12 @@ use serde::{Deserialize, Serialize};
|
||||
#[require(HedzCharacter, DebugInput = DebugInput)]
|
||||
pub struct Player;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
#[derive(Component, Debug, Reflect)]
|
||||
#[reflect(Component)]
|
||||
#[require(LocalInputs)]
|
||||
pub struct LocalPlayer;
|
||||
|
||||
#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[require(Transform, Visibility)]
|
||||
pub struct PlayerBodyMesh;
|
||||
@@ -25,13 +33,6 @@ pub struct PlayerBodyMesh;
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct ClientPlayerId(pub PlayerId);
|
||||
|
||||
/// Client-side only; stores this client's id
|
||||
#[derive(Resource, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
pub struct LocalPlayerId {
|
||||
pub id: PlayerId,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
OnEnter(GameState::Playing),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#[cfg(feature = "client")]
|
||||
use crate::player::LocalPlayer;
|
||||
use crate::{
|
||||
loading_assets::{GameAssets, HeadDropAssets},
|
||||
player::{ClientPlayerId, LocalPlayerId},
|
||||
player::ClientPlayerId,
|
||||
protocol::TbMapEntityMapping,
|
||||
};
|
||||
use avian3d::prelude::Collider;
|
||||
@@ -101,7 +103,8 @@ pub struct PlayerIdMap {
|
||||
pub struct ClientToController<'w, 's> {
|
||||
clients: Query<'w, 's, &'static ClientPlayerId>,
|
||||
players: Res<'w, PlayerIdMap>,
|
||||
local_id: Option<Res<'w, LocalPlayerId>>,
|
||||
#[cfg(feature = "client")]
|
||||
local_id: Option<Single<'w, 's, &'static PlayerId, With<LocalPlayer>>>,
|
||||
}
|
||||
|
||||
impl ClientToController<'_, '_> {
|
||||
@@ -109,7 +112,16 @@ impl ClientToController<'_, '_> {
|
||||
pub fn get_controller(&self, client: ClientId) -> Entity {
|
||||
let player_id = match client.entity() {
|
||||
Some(client) => self.clients.get(client).unwrap().0,
|
||||
None => self.local_id.as_ref().unwrap().id,
|
||||
None => {
|
||||
#[cfg(not(feature = "client"))]
|
||||
{
|
||||
error!("attempted to look up the local controller on a dedicated server");
|
||||
PlayerId { id: 0 }
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
***self.local_id.as_ref().unwrap()
|
||||
}
|
||||
};
|
||||
*self.players.get(&player_id).unwrap()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ use crate::{
|
||||
animation::AnimationFlags,
|
||||
backpack::{Backpack, backpack_ui::BackpackUiState},
|
||||
camera::{CameraArmRotation, CameraTarget},
|
||||
cash::CashResource,
|
||||
cash::CashInventory,
|
||||
character::{AnimatedCharacter, HedzCharacter},
|
||||
control::{
|
||||
ControlState, ControllerSettings, Inputs,
|
||||
BackpackLeftPressed, BackpackRightPressed, BackpackSwapPressed, BackpackTogglePressed,
|
||||
CashHealPressed, ClientInputs, ControllerSettings, Inputs, SelectLeftPressed,
|
||||
SelectRightPressed,
|
||||
controller_common::{MovementSpeedFactor, PlayerCharacterController},
|
||||
},
|
||||
cutscene::StartCutscene,
|
||||
@@ -49,7 +51,14 @@ pub mod events;
|
||||
pub mod messages;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_client_message::<ControlState>(Channel::Unreliable);
|
||||
app.add_client_message::<ClientInputs>(Channel::Unreliable)
|
||||
.add_client_message::<SelectLeftPressed>(Channel::Ordered)
|
||||
.add_client_message::<SelectRightPressed>(Channel::Ordered)
|
||||
.add_client_message::<BackpackTogglePressed>(Channel::Ordered)
|
||||
.add_client_message::<BackpackSwapPressed>(Channel::Ordered)
|
||||
.add_client_message::<BackpackLeftPressed>(Channel::Ordered)
|
||||
.add_client_message::<BackpackRightPressed>(Channel::Ordered)
|
||||
.add_client_message::<CashHealPressed>(Channel::Ordered);
|
||||
|
||||
app.add_client_event::<ClientEnteredPlaying>(Channel::Ordered);
|
||||
|
||||
@@ -89,7 +98,7 @@ pub fn plugin(app: &mut App) {
|
||||
.replicate::<Billboard>()
|
||||
.replicate_once::<CameraArmRotation>()
|
||||
.replicate_once::<CameraTarget>()
|
||||
.replicate::<CashResource>()
|
||||
.replicate::<CashInventory>()
|
||||
.replicate_once::<HedzCharacter>()
|
||||
.replicate_once::<Healing>()
|
||||
.replicate::<Hitpoints>()
|
||||
|
||||
@@ -91,7 +91,11 @@ fn attach_trail(
|
||||
))
|
||||
.id();
|
||||
|
||||
commands.entity(entity).add_child(id);
|
||||
commands
|
||||
.entity(entity)
|
||||
.queue_silenced(move |mut world: EntityWorldMut| {
|
||||
world.add_child(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user