Bevy 0.17 Migration Final PR (#76)
* Get bevy 0.17 compiling and running (#72) * get bevy 0.17 compiling and running * try to fix CI breaking from const assertion for client/server features * fix `bin` -> `lib` for `shared` in CI * typo * fix some collider issues (#73) * Physics/controller improvements (#74) * trying to fix physics prediction * fixed prediction desync * substantial controller improvements * Finish off main bevy 0.17 migration (#75) * fix lookdir issues - airplane moving backwards - player model facing backwards - camera was technically backwards the whole time, and player models were facing the right way; camera is now facing forwards - firing without a target now respects lookdir * fix aim targeting * migrate to bevy_trenchbroom 0.10 crates release * fixed colliders not being adjusted out of worldspace * predict platforms to stop constant rollbacks while riding them * fix key/head drop visuals not working * Fix key/head drop random initial force * fixed static head drops duplicating * fix platform velocity inheritance * fix thrown projectiles not autorotating * fix inconsistent explosion animations * update avian3d to 0.4.1 * fix controller snapping to fixed angle upon switching heads * clean up commented code * fix broken physics positions * Clean comments, fix warnings (#77) * clean comments, fix warnings * fix missing import * steamworks 162 libs * fix mouselook --------- Co-authored-by: extrawurst <mail@rusticorn.com>
This commit is contained in:
@@ -54,7 +54,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
..default()
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(JustifyText::Center),
|
||||
TextLayout::new_with_justify(Justify::Center),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(20.0),
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use avian3d::prelude::{
|
||||
Collider, ColliderAabb, ColliderDensity, ColliderMarker, ColliderMassProperties,
|
||||
CollisionEventsEnabled, CollisionLayers, Sensor,
|
||||
Collider, ColliderAabb, ColliderDensity, ColliderMarker, ColliderOf, ColliderTransform,
|
||||
CollisionEventsEnabled, CollisionLayers, Position, Rotation, Sensor,
|
||||
};
|
||||
use bevy::{
|
||||
ecs::bundle::BundleFromComponents, prelude::*, scene::SceneInstance, utils::synccell::SyncCell,
|
||||
ecs::bundle::BundleFromComponents, platform::cell::SyncCell, prelude::*, scene::SceneInstance,
|
||||
};
|
||||
use bevy_trenchbroom::geometry::Brushes;
|
||||
use lightyear::{
|
||||
frame_interpolation::FrameInterpolate,
|
||||
link::{LinkConditioner, prelude::*},
|
||||
netcode::Key,
|
||||
prelude::{
|
||||
@@ -48,16 +49,22 @@ pub fn plugin(app: &mut App) {
|
||||
);
|
||||
app.add_systems(Last, close_server_processes);
|
||||
app.add_systems(Update, despawn_absent_map_entities);
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
(migrate_remote_entities, ApplyDeferred)
|
||||
.chain()
|
||||
.after(ReplicationSystems::Receive),
|
||||
);
|
||||
|
||||
global_observer!(app, on_connecting);
|
||||
global_observer!(app, on_connection_failed);
|
||||
global_observer!(app, on_connection_succeeded);
|
||||
global_observer!(app, temp_give_player_marker);
|
||||
global_observer!(app, connect_on_local_server_started);
|
||||
global_observer!(app, received_remote_map_entity);
|
||||
global_observer!(app, add_visual_interpolation_components);
|
||||
}
|
||||
|
||||
fn close_server_processes(mut app_exit: EventReader<AppExit>) {
|
||||
fn close_server_processes(mut app_exit: MessageReader<AppExit>) {
|
||||
if app_exit.read().next().is_some() {
|
||||
let mut lock = SERVER_PROCESSES.lock();
|
||||
for mut process in lock.drain(..) {
|
||||
@@ -114,21 +121,22 @@ fn attempt_connection(mut commands: Commands) -> Result {
|
||||
},
|
||||
)?,
|
||||
UdpIo::default(),
|
||||
PredictionManager::default(),
|
||||
InputTimeline(Timeline::from(Input::new(
|
||||
sync_config,
|
||||
InputDelayConfig::balanced(),
|
||||
))),
|
||||
))
|
||||
.trigger(Connect);
|
||||
.trigger(|entity| Connect { entity });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_connection_succeeded(
|
||||
_trigger: Trigger<OnAdd, Connected>,
|
||||
_trigger: On<Add, Connected>,
|
||||
state: Res<State<GameState>>,
|
||||
mut change_state: ResMut<NextState<GameState>>,
|
||||
mut sender: Single<&mut TriggerSender<ClientEnteredPlaying>>,
|
||||
mut sender: Single<&mut EventSender<ClientEnteredPlaying>>,
|
||||
) {
|
||||
if *state == GameState::Connecting {
|
||||
change_state.set(GameState::Playing);
|
||||
@@ -141,21 +149,21 @@ fn on_connection_succeeded(
|
||||
#[derive(Component)]
|
||||
struct ClientActive;
|
||||
|
||||
fn on_connecting(trigger: Trigger<OnAdd, Connecting>, mut commands: Commands) {
|
||||
commands.entity(trigger.target()).insert(ClientActive);
|
||||
fn on_connecting(trigger: On<Add, Connecting>, mut commands: Commands) {
|
||||
commands.entity(trigger.event().entity).insert(ClientActive);
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct LocalServerStdout(SyncCell<mpsc::Receiver<String>>);
|
||||
|
||||
fn on_connection_failed(
|
||||
trigger: Trigger<OnAdd, Disconnected>,
|
||||
trigger: On<Add, Disconnected>,
|
||||
disconnected: Query<&Disconnected>,
|
||||
mut commands: Commands,
|
||||
client_active: Query<&ClientActive>,
|
||||
mut opened_server: Local<bool>,
|
||||
) -> Result {
|
||||
let disconnected = disconnected.get(trigger.target()).unwrap();
|
||||
let disconnected = disconnected.get(trigger.event().entity).unwrap();
|
||||
if *opened_server {
|
||||
panic!(
|
||||
"failed to connect to local server: {:?}",
|
||||
@@ -163,7 +171,7 @@ fn on_connection_failed(
|
||||
);
|
||||
}
|
||||
|
||||
let client = trigger.target();
|
||||
let client = trigger.event().entity;
|
||||
if client_active.contains(client) {
|
||||
commands.entity(client).remove::<ClientActive>();
|
||||
|
||||
@@ -223,73 +231,87 @@ fn parse_local_server_stdout(mut commands: Commands, mut stdout: ResMut<LocalSer
|
||||
}
|
||||
|
||||
fn connect_on_local_server_started(
|
||||
_trigger: Trigger<LocalServerStarted>,
|
||||
_trigger: On<LocalServerStarted>,
|
||||
state: Res<State<GameState>>,
|
||||
mut commands: Commands,
|
||||
client: Single<Entity, With<Client>>,
|
||||
) {
|
||||
if *state == GameState::Connecting {
|
||||
commands.entity(*client).trigger(Connect);
|
||||
commands
|
||||
.entity(*client)
|
||||
.trigger(|entity| Connect { entity });
|
||||
}
|
||||
}
|
||||
|
||||
fn temp_give_player_marker(trigger: Trigger<OnAdd, Player>, mut commands: Commands) {
|
||||
fn temp_give_player_marker(trigger: On<Add, Player>, mut commands: Commands) {
|
||||
commands
|
||||
.entity(trigger.target())
|
||||
.entity(trigger.event().entity)
|
||||
.insert(InputMarker::<ControlState>::default());
|
||||
}
|
||||
|
||||
fn received_remote_map_entity(
|
||||
trigger: Trigger<OnAdd, TbMapEntityId>,
|
||||
world: &mut World,
|
||||
mut child_buffer: Local<Vec<Entity>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn migrate_remote_entities(
|
||||
query: Query<(Entity, &TbMapEntityId), (Added<TbMapEntityId>, With<Replicated>)>,
|
||||
children: Query<&Children>,
|
||||
mut commands: Commands,
|
||||
mut mapping: ResMut<TbMapEntityMapping>,
|
||||
) {
|
||||
let serverside = trigger.target();
|
||||
|
||||
if world.get::<Replicated>(serverside).is_none() {
|
||||
return;
|
||||
for (serverside, tb_id) in query.iter() {
|
||||
received_remote_map_entity(serverside, tb_id.id, &children, &mut mapping, &mut commands);
|
||||
}
|
||||
}
|
||||
|
||||
let id = *world.get::<TbMapEntityId>(serverside).unwrap();
|
||||
|
||||
let Some(clientside) = world.resource_mut::<TbMapEntityMapping>().0.remove(&id.id) else {
|
||||
warn!("received unknown MapEntity ID `{id:?}`");
|
||||
fn received_remote_map_entity(
|
||||
serverside: Entity,
|
||||
tb_id: u64,
|
||||
children: &Query<&Children>,
|
||||
mapping: &mut TbMapEntityMapping,
|
||||
commands: &mut Commands,
|
||||
) {
|
||||
let Some(clientside) = mapping.0.remove(&tb_id) else {
|
||||
warn!("received unknown MapEntity ID `{tb_id:?}`");
|
||||
return;
|
||||
};
|
||||
|
||||
// cannot just use `take` directly with a bundle because then any missing component would cause
|
||||
// the entire bundle to fail
|
||||
move_component::<Brushes>(world, clientside, serverside);
|
||||
move_component::<Brushes>(commands, clientside, serverside);
|
||||
move_component::<(
|
||||
Collider,
|
||||
ColliderAabb,
|
||||
ColliderDensity,
|
||||
ColliderMarker,
|
||||
ColliderMassProperties,
|
||||
CollisionLayers,
|
||||
)>(world, clientside, serverside);
|
||||
move_component::<CollisionEventsEnabled>(world, clientside, serverside);
|
||||
move_component::<Platform>(world, clientside, serverside);
|
||||
move_component::<PlatformTarget>(world, clientside, serverside);
|
||||
move_component::<SceneInstance>(world, clientside, serverside);
|
||||
move_component::<SceneRoot>(world, clientside, serverside);
|
||||
move_component::<Sensor>(world, clientside, serverside);
|
||||
)>(commands, clientside, serverside);
|
||||
move_component::<ColliderOf>(commands, clientside, serverside);
|
||||
move_component::<ColliderTransform>(commands, clientside, serverside);
|
||||
move_component::<CollisionEventsEnabled>(commands, clientside, serverside);
|
||||
move_component::<Platform>(commands, clientside, serverside);
|
||||
move_component::<PlatformTarget>(commands, clientside, serverside);
|
||||
move_component::<SceneInstance>(commands, clientside, serverside);
|
||||
move_component::<SceneRoot>(commands, clientside, serverside);
|
||||
move_component::<Sensor>(commands, clientside, serverside);
|
||||
|
||||
if let Some(children) = world.get::<Children>(clientside) {
|
||||
child_buffer.extend(children.iter());
|
||||
for child in child_buffer.drain(..) {
|
||||
world.entity_mut(child).insert(ChildOf(serverside));
|
||||
if let Ok(children) = children.get(clientside) {
|
||||
for child in children.iter() {
|
||||
commands.entity(child).insert(ChildOf(serverside));
|
||||
}
|
||||
}
|
||||
|
||||
world.entity_mut(clientside).despawn();
|
||||
commands.entity(clientside).despawn();
|
||||
}
|
||||
|
||||
fn move_component<B: Bundle + BundleFromComponents>(world: &mut World, from: Entity, to: Entity) {
|
||||
let comp = world.entity_mut(from).take::<B>();
|
||||
if let Some(comp) = comp {
|
||||
world.entity_mut(to).insert(comp);
|
||||
}
|
||||
fn move_component<B: Bundle + BundleFromComponents>(
|
||||
commands: &mut Commands,
|
||||
from: Entity,
|
||||
to: Entity,
|
||||
) {
|
||||
commands.queue(move |world: &mut World| {
|
||||
let comp = world.entity_mut(from).take::<B>();
|
||||
if let Some(comp) = comp {
|
||||
world.entity_mut(to).insert(comp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn despawn_absent_map_entities(
|
||||
@@ -309,3 +331,22 @@ fn despawn_absent_map_entities(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_visual_interpolation_components(trigger: On<Add, Predicted>, mut commands: Commands) {
|
||||
commands.entity(trigger.entity).insert((
|
||||
FrameInterpolate::<Position> {
|
||||
// We must trigger change detection on visual interpolation
|
||||
// to make sure that child entities (sprites, meshes, text)
|
||||
// are also interpolated
|
||||
trigger_change_detection: true,
|
||||
..default()
|
||||
},
|
||||
FrameInterpolate::<Rotation> {
|
||||
// We must trigger change detection on visual interpolation
|
||||
// to make sure that child entities (sprites, meshes, text)
|
||||
// are also interpolated
|
||||
trigger_change_detection: true,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
54
crates/client/src/control/controller_flying.rs
Normal file
54
crates/client/src/control/controller_flying.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use bevy::prelude::*;
|
||||
use lightyear::prelude::input::native::ActionState;
|
||||
use shared::{
|
||||
GameState,
|
||||
control::{ControlState, ControllerSet, LookDirMovement},
|
||||
player::PlayerBodyMesh,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
rotate_rig
|
||||
.before(shared::control::controller_flying::apply_controls)
|
||||
.in_set(ControllerSet::ApplyControlsFly)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
fn rotate_rig(
|
||||
actions: Query<&ActionState<ControlState>>,
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut player: Query<(&mut Transform, &ChildOf), With<PlayerBodyMesh>>,
|
||||
) {
|
||||
for (mut rig_transform, child_of) in player.iter_mut() {
|
||||
let controls = actions.get(child_of.parent()).unwrap();
|
||||
|
||||
if controls.view_mode {
|
||||
continue;
|
||||
}
|
||||
|
||||
let look_dir = look_dir.0;
|
||||
|
||||
// todo: Make consistent with the running controller
|
||||
let sensitivity = 0.001;
|
||||
let max_pitch = 35.0 * PI / 180.0;
|
||||
let min_pitch = -25.0 * PI / 180.0;
|
||||
|
||||
rig_transform.rotate_y(look_dir.x * -sensitivity);
|
||||
|
||||
let euler_rot = rig_transform.rotation.to_euler(EulerRot::YXZ);
|
||||
let yaw = euler_rot.0;
|
||||
let pitch = euler_rot.1 + look_dir.y * -sensitivity;
|
||||
|
||||
let pitch_clamped = pitch.clamp(min_pitch, max_pitch);
|
||||
rig_transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch_clamped, 0.0);
|
||||
|
||||
// The following can be used to limit the amount of rotation per frame
|
||||
// let target_rotation = rig_transform.rotation
|
||||
// * Quat::from_rotation_x(controls.keyboard_state.look_dir.y * sensitivity);
|
||||
// let clamped_rotation = rig_transform.rotation.rotate_towards(target_rotation, 0.01);
|
||||
// rig_transform.rotation = clamped_rotation;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::Controls;
|
||||
use super::{ControlState, Controls};
|
||||
use crate::{GameState, control::CharacterInputEnabled};
|
||||
use bevy::{
|
||||
input::{
|
||||
@@ -9,10 +9,13 @@ use bevy::{
|
||||
prelude::*,
|
||||
};
|
||||
use lightyear::{
|
||||
input::client::InputSet,
|
||||
input::client::InputSystems,
|
||||
prelude::input::native::{ActionState, InputMarker},
|
||||
};
|
||||
use shared::control::{ControlState, ControllerSet};
|
||||
use shared::{
|
||||
control::{ControllerSet, LookDirMovement},
|
||||
player::PlayerBodyMesh,
|
||||
};
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
@@ -26,18 +29,20 @@ pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
FixedPreUpdate,
|
||||
(
|
||||
reset_lookdir,
|
||||
gamepad_controls,
|
||||
keyboard_controls,
|
||||
mouse_rotate,
|
||||
mouse_click,
|
||||
gamepad_connections.run_if(on_event::<GamepadEvent>),
|
||||
gamepad_connections.run_if(on_message::<GamepadEvent>),
|
||||
combine_controls,
|
||||
get_lookdir,
|
||||
clear_keyboard_just,
|
||||
clear_gamepad_just,
|
||||
)
|
||||
.chain()
|
||||
.in_set(ControllerSet::CollectInputs)
|
||||
.before(InputSet::WriteClientInputs)
|
||||
.before(InputSystems::WriteClientInputs)
|
||||
.run_if(
|
||||
in_state(GameState::Playing)
|
||||
.and(resource_exists_and_equals(CharacterInputEnabled::On)),
|
||||
@@ -45,7 +50,7 @@ pub fn plugin(app: &mut App) {
|
||||
)
|
||||
.add_systems(
|
||||
FixedPreUpdate,
|
||||
buffer_inputs.in_set(InputSet::WriteClientInputs),
|
||||
buffer_inputs.in_set(InputSystems::WriteClientInputs),
|
||||
);
|
||||
|
||||
app.add_systems(
|
||||
@@ -63,6 +68,10 @@ fn buffer_inputs(
|
||||
player.0 = *controls;
|
||||
}
|
||||
|
||||
fn reset_lookdir(mut look_dir: ResMut<LookDirMovement>) {
|
||||
look_dir.0 = Vec2::ZERO;
|
||||
}
|
||||
|
||||
/// Reset character inputs to default when character input is disabled.
|
||||
fn reset_control_state_on_disable(
|
||||
state: Res<CharacterInputEnabled>,
|
||||
@@ -71,7 +80,10 @@ fn reset_control_state_on_disable(
|
||||
) {
|
||||
if state.is_changed() && matches!(*state, CharacterInputEnabled::Off) {
|
||||
*controls = Controls::default();
|
||||
*control_state = ControlState::default();
|
||||
*control_state = ControlState {
|
||||
look_dir: control_state.look_dir,
|
||||
..default()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +177,7 @@ fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<Contr
|
||||
let keyboard = controls.keyboard_state;
|
||||
let gamepad = controls.gamepad_state.unwrap_or_default();
|
||||
|
||||
combined_controls.look_dir = gamepad.look_dir + keyboard.look_dir;
|
||||
combined_controls.look_dir = Dir3::NEG_Z;
|
||||
combined_controls.move_dir = gamepad.move_dir + keyboard.move_dir;
|
||||
combined_controls.jump = gamepad.jump | keyboard.jump;
|
||||
combined_controls.view_mode = gamepad.view_mode | keyboard.view_mode;
|
||||
@@ -180,6 +192,17 @@ fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<Contr
|
||||
combined_controls.cash_heal = gamepad.cash_heal | keyboard.cash_heal;
|
||||
}
|
||||
|
||||
fn get_lookdir(
|
||||
mut controls: ResMut<ControlState>,
|
||||
rig_transform: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
||||
) {
|
||||
controls.look_dir = if let Some(ref rig_transform) = rig_transform {
|
||||
rig_transform.forward()
|
||||
} else {
|
||||
Dir3::NEG_Z
|
||||
};
|
||||
}
|
||||
|
||||
/// Applies a square deadzone to a Vec2
|
||||
fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
|
||||
Vec2::new(
|
||||
@@ -192,6 +215,7 @@ fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
|
||||
fn gamepad_controls(
|
||||
gamepads: Query<(Entity, &Gamepad, &InputStateCache<GamepadButton>)>,
|
||||
mut controls: ResMut<Controls>,
|
||||
mut look_dir: ResMut<LookDirMovement>,
|
||||
) {
|
||||
let Some((_e, gamepad, cache)) = gamepads.iter().next() else {
|
||||
if controls.gamepad_state.is_some() {
|
||||
@@ -208,7 +232,7 @@ fn gamepad_controls(
|
||||
.unwrap_or_default();
|
||||
|
||||
// 8BitDo Ultimate wireless Controller for PC
|
||||
let look_dir = if gamepad.vendor_id() == Some(11720) && gamepad.product_id() == Some(12306) {
|
||||
look_dir.0 = if gamepad.vendor_id() == Some(11720) && gamepad.product_id() == Some(12306) {
|
||||
const EPSILON: f32 = 0.015;
|
||||
Vec2::new(
|
||||
if rotate < 0.5 - EPSILON {
|
||||
@@ -224,9 +248,11 @@ fn gamepad_controls(
|
||||
deadzone_square(gamepad.right_stick(), deadzone_right_stick) * 40.
|
||||
};
|
||||
|
||||
let move_dir = deadzone_square(gamepad.left_stick(), deadzone_left_stick);
|
||||
|
||||
let state = ControlState {
|
||||
move_dir: deadzone_square(gamepad.left_stick(), deadzone_left_stick),
|
||||
look_dir,
|
||||
move_dir,
|
||||
look_dir: Dir3::NEG_Z,
|
||||
jump: gamepad.pressed(GamepadButton::South),
|
||||
view_mode: gamepad.pressed(GamepadButton::LeftTrigger2),
|
||||
trigger: gamepad.pressed(GamepadButton::RightTrigger2),
|
||||
@@ -251,11 +277,9 @@ fn gamepad_controls(
|
||||
}
|
||||
|
||||
/// Collect mouse movement input
|
||||
fn mouse_rotate(mut mouse: EventReader<MouseMotion>, mut controls: ResMut<Controls>) {
|
||||
controls.keyboard_state.look_dir = Vec2::ZERO;
|
||||
|
||||
fn mouse_rotate(mut mouse: MessageReader<MouseMotion>, mut look_dir: ResMut<LookDirMovement>) {
|
||||
for ev in mouse.read() {
|
||||
controls.keyboard_state.look_dir += ev.delta;
|
||||
look_dir.0 += ev.delta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +316,7 @@ fn keyboard_controls(
|
||||
}
|
||||
|
||||
/// Collect mouse button input when pressed
|
||||
fn mouse_click(mut events: EventReader<MouseButtonInput>, mut controls: ResMut<Controls>) {
|
||||
fn mouse_click(mut events: MessageReader<MouseButtonInput>, mut controls: ResMut<Controls>) {
|
||||
controls.keyboard_state.just_triggered = false;
|
||||
|
||||
for ev in events.read() {
|
||||
@@ -318,7 +342,7 @@ fn mouse_click(mut events: EventReader<MouseButtonInput>, mut controls: ResMut<C
|
||||
}
|
||||
|
||||
/// Receive gamepad connections and disconnections
|
||||
fn gamepad_connections(mut evr_gamepad: EventReader<GamepadEvent>) {
|
||||
fn gamepad_connections(mut evr_gamepad: MessageReader<GamepadEvent>) {
|
||||
for ev in evr_gamepad.read() {
|
||||
if let GamepadEvent::Connection(connection) = ev {
|
||||
match &connection.connection {
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
use shared::control::{ControlState, ControllerSet};
|
||||
|
||||
mod controller_flying;
|
||||
pub mod controls;
|
||||
|
||||
#[derive(Resource, Debug, Default)]
|
||||
@@ -19,7 +20,7 @@ pub enum CharacterInputEnabled {
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.insert_resource(CharacterInputEnabled::On);
|
||||
|
||||
app.add_plugins(controls::plugin);
|
||||
app.add_plugins((controller_flying::plugin, controls::plugin));
|
||||
|
||||
app.configure_sets(
|
||||
FixedPreUpdate,
|
||||
|
||||
@@ -28,7 +28,7 @@ fn setup(mut commands: Commands) {
|
||||
font_size: 12.0,
|
||||
..default()
|
||||
},
|
||||
TextLayout::new_with_justify(JustifyText::Left),
|
||||
TextLayout::new_with_justify(Justify::Left),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(5.0),
|
||||
|
||||
@@ -67,16 +67,16 @@ fn on_added(
|
||||
}
|
||||
|
||||
fn on_removed(
|
||||
trigger: Trigger<OnRemove, Healing>,
|
||||
trigger: On<Remove, Healing>,
|
||||
mut commands: Commands,
|
||||
effects: Query<&HasHealingEffects>,
|
||||
) {
|
||||
let Ok(has_effects) = effects.get(trigger.target()) else {
|
||||
let Ok(has_effects) = effects.get(trigger.event().entity) else {
|
||||
return;
|
||||
};
|
||||
commands.entity(has_effects.effects).try_despawn();
|
||||
commands
|
||||
.entity(trigger.target())
|
||||
.entity(trigger.event().entity)
|
||||
.remove::<HasHealingEffects>();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,13 @@ use bevy::{
|
||||
use bevy_common_assets::ron::RonAssetPlugin;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
use bevy_ui_gradients::UiGradientsPlugin;
|
||||
use bevy_trenchbroom_avian::AvianPhysicsBackend;
|
||||
use camera::MainCamera;
|
||||
use heads_database::HeadDatabaseAsset;
|
||||
use lightyear::prelude::client::ClientPlugins;
|
||||
use lightyear::{
|
||||
avian3d::plugin::LightyearAvianPlugin, frame_interpolation::FrameInterpolationPlugin,
|
||||
prelude::client::ClientPlugins,
|
||||
};
|
||||
use loading_assets::AudioAssets;
|
||||
use shared::*;
|
||||
use std::time::Duration;
|
||||
@@ -55,6 +58,7 @@ fn main() {
|
||||
level: bevy::log::Level::INFO,
|
||||
// provide custom log layer to receive logging events
|
||||
custom_layer: bevy_debug_log::log_capture_layer,
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -64,22 +68,37 @@ fn main() {
|
||||
bevy_debug_log::LogViewerPlugin::default()
|
||||
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
|
||||
);
|
||||
app.add_plugins(PhysicsPlugins::default());
|
||||
app.add_plugins(
|
||||
PhysicsPlugins::default()
|
||||
.build()
|
||||
// TODO: This plugin is *not* disabled on the server. This is to solve a bug related to collider transform inheritance. See the
|
||||
// LightyearAvianPlugin below.
|
||||
// Periwink is looking into it at the moment. This **must** be disabled on the client, or positions break for NPCs and some other things.
|
||||
.disable::<PhysicsTransformPlugin>()
|
||||
// FrameInterpolation handles interpolating Position and Rotation
|
||||
.disable::<PhysicsInterpolationPlugin>(),
|
||||
);
|
||||
// TODO: This plugin is *not* inserted on the server. This is to solve a bug related to collider transform inheritance. See the
|
||||
// `.disable::<PhysicsTransformPlugin>()` above.
|
||||
// Periwink is looking into it at the moment. This **must** be inserted on the client, or positions break for NPCs and some other things.
|
||||
app.add_plugins(LightyearAvianPlugin::default());
|
||||
app.add_plugins(FrameInterpolationPlugin::<Position>::default());
|
||||
app.add_plugins(FrameInterpolationPlugin::<Rotation>::default());
|
||||
app.add_plugins(ClientPlugins {
|
||||
tick_duration: Duration::from_secs_f64(1.0 / 60.0),
|
||||
});
|
||||
app.add_plugins(Sprite3dPlugin);
|
||||
app.add_plugins(TrenchBroomPlugins(
|
||||
TrenchBroomConfig::new("hedz").icon(None),
|
||||
TrenchBroomConfig::new("hedz")
|
||||
.icon(None)
|
||||
.default_solid_spawn_hooks(|| SpawnHooks::new().convex_collider()),
|
||||
));
|
||||
app.add_plugins(UiGradientsPlugin);
|
||||
app.add_plugins(TrenchBroomPhysicsPlugin::new(AvianPhysicsBackend));
|
||||
app.add_plugins(RonAssetPlugin::<HeadDatabaseAsset>::new(&["headsdb.ron"]));
|
||||
|
||||
#[cfg(feature = "dbg")]
|
||||
{
|
||||
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin {
|
||||
enable_multipass_for_primary_context: true,
|
||||
});
|
||||
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin::default());
|
||||
app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new());
|
||||
app.add_plugins(PhysicsDebugPlugin::default());
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ pub enum PlayerAssignmentState {
|
||||
pub struct LocalPlayer;
|
||||
|
||||
fn on_update_head_mesh(
|
||||
trigger: Trigger<ClientHeadChanged>,
|
||||
trigger: On<ClientHeadChanged>,
|
||||
mut commands: Commands,
|
||||
body_mesh: Single<(Entity, &Children), With<PlayerBodyMesh>>,
|
||||
head_db: Res<HeadsDatabase>,
|
||||
|
||||
@@ -7,7 +7,7 @@ pub fn plugin(app: &mut App) {
|
||||
}
|
||||
|
||||
fn on_spawn_sounds(
|
||||
trigger: Trigger<PlaySound>,
|
||||
trigger: On<PlaySound>,
|
||||
mut commands: Commands,
|
||||
// settings: SettingsRead,
|
||||
assets: Res<AudioAssets>,
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn plugin(app: &mut App) {
|
||||
);
|
||||
}
|
||||
|
||||
fn log_steam_events(mut events: EventReader<SteamworksEvent>) {
|
||||
fn log_steam_events(mut events: MessageReader<SteamworksEvent>) {
|
||||
for e in events.read() {
|
||||
info!("steam ev: {:?}", e);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgba(0., 0., 0., 0.6)),
|
||||
StateScoped(PauseMenuState::Open),
|
||||
DespawnOnExit(PauseMenuState::Open),
|
||||
children![
|
||||
spawn_progress(ProgressBar::Music, 100, assets.font.clone()),
|
||||
spawn_progress(ProgressBar::Sound, 80, assets.font.clone())
|
||||
@@ -91,7 +91,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
},
|
||||
BackgroundColor(BLACK.into()),
|
||||
BorderRadius::all(Val::Px(100.)),
|
||||
BorderColor(HEDZ_PURPLE.into()),
|
||||
BorderColor::all(HEDZ_PURPLE),
|
||||
BoxShadow::new(
|
||||
BLACK.into(),
|
||||
Val::Px(2.),
|
||||
@@ -117,7 +117,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
..Default::default()
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(JustifyText::Left),
|
||||
TextLayout::new_with_justify(Justify::Left),
|
||||
),
|
||||
(
|
||||
Node {
|
||||
@@ -131,7 +131,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
..Default::default()
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(),
|
||||
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
||||
),
|
||||
(
|
||||
Text::new(format!("{value}",)),
|
||||
@@ -141,7 +141,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
..Default::default()
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(),
|
||||
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
||||
),
|
||||
(
|
||||
Node {
|
||||
@@ -155,7 +155,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
..Default::default()
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(),
|
||||
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
||||
)
|
||||
],
|
||||
)
|
||||
@@ -176,11 +176,11 @@ fn selection_changed(
|
||||
) {
|
||||
if state.is_changed() {
|
||||
for (mut border, bar) in query.iter_mut() {
|
||||
border.0 = if *bar == state.0 {
|
||||
HEDZ_GREEN.into()
|
||||
*border = BorderColor::all(if *bar == state.0 {
|
||||
HEDZ_GREEN
|
||||
} else {
|
||||
HEDZ_PURPLE.into()
|
||||
}
|
||||
HEDZ_PURPLE
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user