Controller Replication (#57)
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -3665,10 +3665,11 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "happy_feet"
|
name = "happy_feet"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/PROMETHIA-27/happy_feet.git?rev=5a87760d8a7970c74e07f30bc31ceaafad9a69b6#5a87760d8a7970c74e07f30bc31ceaafad9a69b6"
|
source = "git+https://github.com/atornity/happy_feet.git?rev=1b24ed95f166e63af35e7b6f9f0053d6d28e1f1a#1b24ed95f166e63af35e7b6f9f0053d6d28e1f1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"avian3d",
|
"avian3d",
|
||||||
"bevy",
|
"bevy",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4411,6 +4412,7 @@ dependencies = [
|
|||||||
"lightyear_interpolation",
|
"lightyear_interpolation",
|
||||||
"lightyear_link",
|
"lightyear_link",
|
||||||
"lightyear_messages",
|
"lightyear_messages",
|
||||||
|
"lightyear_prediction",
|
||||||
"lightyear_replication",
|
"lightyear_replication",
|
||||||
"lightyear_sync",
|
"lightyear_sync",
|
||||||
"lightyear_transport",
|
"lightyear_transport",
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ bevy_common_assets = { version = "0.13.0", features = ["ron"] }
|
|||||||
bevy_debug_log = "0.6.0"
|
bevy_debug_log = "0.6.0"
|
||||||
bevy_sprite3d = "5.0.0"
|
bevy_sprite3d = "5.0.0"
|
||||||
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
|
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
|
||||||
happy_feet = { git = "https://github.com/PROMETHIA-27/happy_feet.git", rev = "5a87760d8a7970c74e07f30bc31ceaafad9a69b6" }
|
happy_feet = { git = "https://github.com/atornity/happy_feet.git", rev = "1b24ed95f166e63af35e7b6f9f0053d6d28e1f1a", features = [
|
||||||
|
"serde",
|
||||||
|
] }
|
||||||
lightyear = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f", default-features = false, features = [
|
lightyear = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f", default-features = false, features = [
|
||||||
"interpolation",
|
"interpolation",
|
||||||
"netcode",
|
"netcode",
|
||||||
|
"prediction",
|
||||||
"replication",
|
"replication",
|
||||||
"std",
|
"std",
|
||||||
"steam",
|
"steam",
|
||||||
|
|||||||
@@ -1,20 +1,37 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use lightyear::{
|
use lightyear::{
|
||||||
|
connection::client::ClientState,
|
||||||
netcode::Key,
|
netcode::Key,
|
||||||
prelude::{client::NetcodeConfig, *},
|
prelude::{client::NetcodeConfig, *},
|
||||||
};
|
};
|
||||||
|
use shared::{GameState, heads_database::HeadsDatabase, tb_entities::SpawnPoint};
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Startup, temp_connect_on_startup);
|
app.add_systems(Startup, temp_connect_on_startup);
|
||||||
|
app.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
spawn_disconnected_player.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn temp_connect_on_startup(mut commands: Commands) -> Result {
|
fn temp_connect_on_startup(mut commands: Commands) -> Result {
|
||||||
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25564);
|
let mut args = std::env::args();
|
||||||
|
let client_port = loop {
|
||||||
|
match args.next().as_deref() {
|
||||||
|
Some("--port") => {
|
||||||
|
break args.next().unwrap().parse::<u16>().unwrap();
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => break 25564,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), client_port);
|
||||||
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
|
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
|
||||||
let auth = Authentication::Manual {
|
let auth = Authentication::Manual {
|
||||||
server_addr,
|
server_addr,
|
||||||
client_id: 0,
|
client_id: client_port as u64,
|
||||||
private_key: Key::default(),
|
private_key: Key::default(),
|
||||||
protocol_id: 0,
|
protocol_id: 0,
|
||||||
};
|
};
|
||||||
@@ -26,10 +43,28 @@ fn temp_connect_on_startup(mut commands: Commands) -> Result {
|
|||||||
PeerAddr(server_addr),
|
PeerAddr(server_addr),
|
||||||
Link::new(None),
|
Link::new(None),
|
||||||
ReplicationReceiver::default(),
|
ReplicationReceiver::default(),
|
||||||
client::NetcodeClient::new(auth, NetcodeConfig::default())?,
|
client::NetcodeClient::new(
|
||||||
|
auth,
|
||||||
|
NetcodeConfig {
|
||||||
|
client_timeout_secs: 1,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
)?,
|
||||||
UdpIo::default(),
|
UdpIo::default(),
|
||||||
))
|
))
|
||||||
.trigger(Connect);
|
.trigger(Connect);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_disconnected_player(
|
||||||
|
disconnected: Single<&Client, Changed<Client>>,
|
||||||
|
commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
query: Query<&Transform, With<SpawnPoint>>,
|
||||||
|
heads_db: Res<HeadsDatabase>,
|
||||||
|
) {
|
||||||
|
if disconnected.state == ClientState::Disconnected {
|
||||||
|
shared::player::spawn(commands, query, asset_server, heads_db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use lightyear::prelude::{
|
|||||||
server::{NetcodeConfig, NetcodeServer, ServerUdpIo},
|
server::{NetcodeConfig, NetcodeServer, ServerUdpIo},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use shared::utils::commands::IsServer;
|
use shared::{heads_database::HeadsDatabase, tb_entities::SpawnPoint, utils::commands::IsServer};
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
@@ -17,6 +17,9 @@ fn handle_new_client(
|
|||||||
trigger: Trigger<OnAdd, Connected>,
|
trigger: Trigger<OnAdd, Connected>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
id: Query<&PeerAddr>,
|
id: Query<&PeerAddr>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
query: Query<&Transform, With<SpawnPoint>>,
|
||||||
|
heads_db: Res<HeadsDatabase>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let id = id.get(trigger.target())?;
|
let id = id.get(trigger.target())?;
|
||||||
|
|
||||||
@@ -26,6 +29,8 @@ fn handle_new_client(
|
|||||||
.entity(trigger.target())
|
.entity(trigger.target())
|
||||||
.insert(ReplicationSender::default());
|
.insert(ReplicationSender::default());
|
||||||
|
|
||||||
|
shared::player::spawn(commands, query, asset_server, heads_db);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug)]
|
#[derive(Component, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct CameraTarget;
|
pub struct CameraTarget;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug)]
|
#[derive(Component, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct CameraArmRotation;
|
pub struct CameraArmRotation;
|
||||||
|
|
||||||
/// Requested camera rotation based on various input sources (keyboard, gamepad)
|
/// Requested camera rotation based on various input sources (keyboard, gamepad)
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ use bevy::{
|
|||||||
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
|
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
|
||||||
prelude::*, scene::SceneInstanceReady,
|
prelude::*, scene::SceneInstanceReady,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{f32::consts::PI, time::Duration};
|
use std::{f32::consts::PI, time::Duration};
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct ProjectileOrigin;
|
pub struct ProjectileOrigin;
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct AnimatedCharacter {
|
pub struct AnimatedCharacter {
|
||||||
head: usize,
|
head: usize,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,22 @@ use crate::{
|
|||||||
animation::AnimationFlags,
|
animation::AnimationFlags,
|
||||||
character::HasCharacterAnimations,
|
character::HasCharacterAnimations,
|
||||||
control::{Controls, SelectedController, controls::ControllerSettings},
|
control::{Controls, SelectedController, controls::ControllerSettings},
|
||||||
heads_database::HeadControls,
|
head::ActiveHead,
|
||||||
|
heads_database::{HeadControls, HeadsDatabase},
|
||||||
|
physics_layers::GameLayer,
|
||||||
player::{Player, PlayerBodyMesh},
|
player::{Player, PlayerBodyMesh},
|
||||||
};
|
};
|
||||||
use avian3d::{math::*, prelude::*};
|
use avian3d::{math::*, prelude::*};
|
||||||
use bevy::prelude::*;
|
use bevy::{
|
||||||
|
ecs::{component::HookContext, world::DeferredWorld},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use happy_feet::prelude::{
|
use happy_feet::prelude::{
|
||||||
Character, CharacterDrag, CharacterGravity, CharacterMovement, CharacterPlugins,
|
Character, CharacterDrag, CharacterGravity, CharacterMovement, CharacterPlugins,
|
||||||
GroundFriction, Grounding, GroundingConfig, KinematicVelocity, MoveInput, SteppingBehaviour,
|
GroundFriction, Grounding, GroundingConfig, KinematicVelocity, MoveInput, SteppingBehaviour,
|
||||||
SteppingConfig,
|
SteppingConfig,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins(CharacterPlugins::default());
|
app.add_plugins(CharacterPlugins::default());
|
||||||
@@ -148,10 +154,28 @@ fn decelerate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct MovementSpeedFactor(pub f32);
|
pub struct MovementSpeedFactor(pub f32);
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
#[component(on_add = add_controller_bundle)]
|
||||||
|
pub struct PlayerCharacterController;
|
||||||
|
|
||||||
|
fn add_controller_bundle(mut world: DeferredWorld, ctx: HookContext) {
|
||||||
|
let head = world
|
||||||
|
.entity(ctx.entity)
|
||||||
|
.get::<ActiveHead>()
|
||||||
|
.expect("player must be spawned with an `ActiveHead`")
|
||||||
|
.0;
|
||||||
|
let controls = world.resource::<HeadsDatabase>().head_stats(head).controls;
|
||||||
|
world
|
||||||
|
.commands()
|
||||||
|
.entity(ctx.entity)
|
||||||
|
.insert(CharacterControllerBundle::new(controls));
|
||||||
|
}
|
||||||
|
|
||||||
/// A bundle that contains the components needed for a basic
|
/// A bundle that contains the components needed for a basic
|
||||||
/// kinematic character controller.
|
/// kinematic character controller.
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
@@ -163,11 +187,13 @@ pub struct CharacterControllerBundle {
|
|||||||
collision_events: CollisionEventsEnabled,
|
collision_events: CollisionEventsEnabled,
|
||||||
movement_config: MovementConfig,
|
movement_config: MovementConfig,
|
||||||
interpolation: TransformInterpolation,
|
interpolation: TransformInterpolation,
|
||||||
|
layers: CollisionLayers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterControllerBundle {
|
impl CharacterControllerBundle {
|
||||||
pub fn new(collider: Collider, controls: HeadControls) -> Self {
|
pub fn new(controls: HeadControls) -> Self {
|
||||||
// Create shape caster as a slightly smaller version of collider
|
// Create shape caster as a slightly smaller version of collider
|
||||||
|
let collider = Collider::capsule(0.9, 1.2);
|
||||||
let mut caster_shape = collider.clone();
|
let mut caster_shape = collider.clone();
|
||||||
caster_shape.set_scale(Vector::ONE * 0.98, 10);
|
caster_shape.set_scale(Vector::ONE * 0.98, 10);
|
||||||
|
|
||||||
@@ -184,6 +210,10 @@ impl CharacterControllerBundle {
|
|||||||
collision_events: CollisionEventsEnabled,
|
collision_events: CollisionEventsEnabled,
|
||||||
movement_config: config,
|
movement_config: config,
|
||||||
interpolation: TransformInterpolation,
|
interpolation: TransformInterpolation,
|
||||||
|
layers: CollisionLayers::new(
|
||||||
|
LayerMask(GameLayer::Player.to_bits()),
|
||||||
|
LayerMask::ALL & !GameLayer::CollectiblePhysics.to_bits(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use bevy::{
|
|||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.init_resource::<Controls>();
|
app.init_resource::<Controls>();
|
||||||
@@ -43,7 +44,7 @@ pub fn plugin(app: &mut App) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, PartialEq, Reflect)]
|
#[derive(Component, Clone, PartialEq, Reflect, Serialize, Deserialize)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct ControllerSettings {
|
pub struct ControllerSettings {
|
||||||
pub deceleration_factor: f32,
|
pub deceleration_factor: f32,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct ActiveHead(pub usize);
|
pub struct ActiveHead(pub usize);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::{
|
|||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub static HEAD_COUNT: usize = 18;
|
pub static HEAD_COUNT: usize = 18;
|
||||||
pub static HEAD_SLOTS: usize = 5;
|
pub static HEAD_SLOTS: usize = 5;
|
||||||
@@ -21,7 +22,7 @@ pub struct HeadsImages {
|
|||||||
pub heads: Vec<Handle<Image>>,
|
pub heads: Vec<Handle<Image>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
|
#[derive(Clone, Copy, Debug, PartialEq, Reflect, Serialize, Deserialize)]
|
||||||
pub struct HeadState {
|
pub struct HeadState {
|
||||||
pub head: usize,
|
pub head: usize,
|
||||||
pub health: u32,
|
pub health: u32,
|
||||||
@@ -51,7 +52,7 @@ impl HeadState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Default, Reflect, Debug)]
|
#[derive(Component, Default, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct ActiveHeads {
|
pub struct ActiveHeads {
|
||||||
heads: [Option<HeadState>; 5],
|
heads: [Option<HeadState>; 5],
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
camera::{CameraArmRotation, CameraTarget},
|
camera::{CameraArmRotation, CameraTarget},
|
||||||
cash::{Cash, CashCollectEvent},
|
cash::{Cash, CashCollectEvent},
|
||||||
character::AnimatedCharacter,
|
character::AnimatedCharacter,
|
||||||
control::controller_common::CharacterControllerBundle,
|
control::controller_common::PlayerCharacterController,
|
||||||
global_observer,
|
global_observer,
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
head_drop::HeadDrops,
|
head_drop::HeadDrops,
|
||||||
@@ -12,9 +12,9 @@ use crate::{
|
|||||||
hitpoints::{Hitpoints, Kill},
|
hitpoints::{Hitpoints, Kill},
|
||||||
loading_assets::AudioAssets,
|
loading_assets::AudioAssets,
|
||||||
npc::SpawnCharacter,
|
npc::SpawnCharacter,
|
||||||
physics_layers::GameLayer,
|
|
||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
tb_entities::SpawnPoint,
|
tb_entities::SpawnPoint,
|
||||||
|
utils::commands::EntityCommandExt,
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{
|
use bevy::{
|
||||||
@@ -22,17 +22,18 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
window::{CursorGrabMode, PrimaryWindow},
|
window::{CursorGrabMode, PrimaryWindow},
|
||||||
};
|
};
|
||||||
|
use lightyear::prelude::{NetworkTarget, PredictionTarget, Replicate};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Player;
|
pub struct Player;
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
|
||||||
#[require(Transform, Visibility)]
|
#[require(Transform, Visibility)]
|
||||||
pub struct PlayerBodyMesh;
|
pub struct PlayerBodyMesh;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Startup, (toggle_cursor_system, cursor_recenter));
|
app.add_systems(Startup, (toggle_cursor_system, cursor_recenter));
|
||||||
app.add_systems(OnEnter(GameState::Playing), spawn);
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
@@ -46,10 +47,10 @@ pub fn plugin(app: &mut App) {
|
|||||||
global_observer!(app, on_update_head_mesh);
|
global_observer!(app, on_update_head_mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
pub fn spawn(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
query: Query<&Transform, With<SpawnPoint>>,
|
query: Query<&Transform, With<SpawnPoint>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
) {
|
) {
|
||||||
let Some(spawn) = query.iter().next() else {
|
let Some(spawn) = query.iter().next() else {
|
||||||
@@ -58,10 +59,24 @@ fn spawn(
|
|||||||
|
|
||||||
let transform = Transform::from_translation(spawn.translation + Vec3::new(0., 3., 0.));
|
let transform = Transform::from_translation(spawn.translation + Vec3::new(0., 3., 0.));
|
||||||
|
|
||||||
let collider = Collider::capsule(0.9, 1.2);
|
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn(player_bundle(transform, &heads_db))
|
||||||
|
.insert_server((
|
||||||
|
Replicate::to_clients(NetworkTarget::All),
|
||||||
|
PredictionTarget::to_clients(NetworkTarget::All),
|
||||||
|
))
|
||||||
|
.observe(on_kill);
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
|
||||||
|
PlaybackSettings::DESPAWN,
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.trigger(SpawnCharacter(transform.translation));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_bundle(transform: Transform, heads_db: &Res<HeadsDatabase>) -> impl Bundle {
|
||||||
|
(
|
||||||
Name::from("player"),
|
Name::from("player"),
|
||||||
Player,
|
Player,
|
||||||
ActiveHead(0),
|
ActiveHead(0),
|
||||||
@@ -76,26 +91,14 @@ fn spawn(
|
|||||||
CameraTarget,
|
CameraTarget,
|
||||||
transform,
|
transform,
|
||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
CollisionLayers::new(
|
PlayerCharacterController,
|
||||||
LayerMask(GameLayer::Player.to_bits()),
|
|
||||||
LayerMask::ALL & !GameLayer::CollectiblePhysics.to_bits(),
|
|
||||||
),
|
|
||||||
CharacterControllerBundle::new(collider, heads_db.head_stats(0).controls),
|
|
||||||
children![(
|
children![(
|
||||||
Name::new("player-rig"),
|
Name::new("player-rig"),
|
||||||
PlayerBodyMesh,
|
PlayerBodyMesh,
|
||||||
CameraArmRotation,
|
CameraArmRotation,
|
||||||
children![AnimatedCharacter::new(0)]
|
children![AnimatedCharacter::new(0)]
|
||||||
)],
|
)],
|
||||||
))
|
)
|
||||||
.observe(on_kill);
|
|
||||||
|
|
||||||
commands.spawn((
|
|
||||||
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
|
|
||||||
PlaybackSettings::DESPAWN,
|
|
||||||
));
|
|
||||||
|
|
||||||
commands.trigger(SpawnCharacter(transform.translation));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_kill(
|
fn on_kill(
|
||||||
|
|||||||
@@ -1,20 +1,72 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
abilities::BuildExplosionSprite, global_observer, loading_assets::GameAssets,
|
abilities::BuildExplosionSprite,
|
||||||
|
camera::{CameraArmRotation, CameraTarget},
|
||||||
|
character::AnimatedCharacter,
|
||||||
|
control::{
|
||||||
|
controller_common::{MovementSpeedFactor, PlayerCharacterController},
|
||||||
|
controls::ControllerSettings,
|
||||||
|
},
|
||||||
|
global_observer,
|
||||||
|
head::ActiveHead,
|
||||||
|
heads::ActiveHeads,
|
||||||
|
loading_assets::GameAssets,
|
||||||
|
player::{Player, PlayerBodyMesh},
|
||||||
utils::triggers::TriggerAppExt,
|
utils::triggers::TriggerAppExt,
|
||||||
};
|
};
|
||||||
|
use avian3d::prelude::{AngularVelocity, CollisionLayers, LinearVelocity};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use lightyear::prelude::{ActionsChannel, AppComponentExt};
|
use happy_feet::{
|
||||||
|
grounding::GroundingState,
|
||||||
|
prelude::{
|
||||||
|
Character, CharacterDrag, CharacterGravity, CharacterMovement, GroundFriction, Grounding,
|
||||||
|
GroundingConfig, KinematicVelocity, MoveInput, SteppingConfig,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use lightyear::prelude::{
|
||||||
|
ActionsChannel, AppComponentExt, PredictionMode, PredictionRegistrationExt,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.register_component::<Transform>();
|
app.register_component::<Transform>()
|
||||||
|
.add_prediction(PredictionMode::Full)
|
||||||
|
.add_should_rollback(transform_should_rollback);
|
||||||
app.register_component::<GltfSceneRoot>();
|
app.register_component::<GltfSceneRoot>();
|
||||||
|
app.register_component::<Player>();
|
||||||
|
app.register_component::<PlayerBodyMesh>();
|
||||||
|
app.register_component::<Name>();
|
||||||
|
app.register_component::<ActiveHead>();
|
||||||
|
app.register_component::<ActiveHeads>();
|
||||||
|
app.register_component::<CameraTarget>();
|
||||||
|
app.register_component::<CameraArmRotation>();
|
||||||
|
app.register_component::<CollisionLayers>();
|
||||||
|
app.register_component::<AnimatedCharacter>();
|
||||||
|
app.register_component::<PlayerCharacterController>();
|
||||||
|
app.register_component::<LinearVelocity>();
|
||||||
|
app.register_component::<AngularVelocity>();
|
||||||
|
app.register_component::<KinematicVelocity>();
|
||||||
|
app.register_component::<Character>();
|
||||||
|
app.register_component::<MoveInput>();
|
||||||
|
app.register_component::<MovementSpeedFactor>();
|
||||||
|
app.register_component::<CharacterMovement>();
|
||||||
|
app.register_component::<SteppingConfig>();
|
||||||
|
app.register_component::<GroundingConfig>();
|
||||||
|
app.register_component::<Grounding>();
|
||||||
|
app.register_component::<GroundingState>();
|
||||||
|
app.register_component::<CharacterGravity>();
|
||||||
|
app.register_component::<GroundFriction>();
|
||||||
|
app.register_component::<CharacterDrag>();
|
||||||
|
app.register_component::<ControllerSettings>();
|
||||||
|
|
||||||
app.replicate_trigger::<BuildExplosionSprite, ActionsChannel>();
|
app.replicate_trigger::<BuildExplosionSprite, ActionsChannel>();
|
||||||
|
|
||||||
global_observer!(app, spawn_gltf_scene_roots);
|
global_observer!(app, spawn_gltf_scene_roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transform_should_rollback(this: &Transform, that: &Transform) -> bool {
|
||||||
|
this.translation.distance_squared(that.translation) >= 0.01f32.powf(2.)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect, Serialize, Deserialize, PartialEq)]
|
#[derive(Component, Reflect, Serialize, Deserialize, PartialEq)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub enum GltfSceneRoot {
|
pub enum GltfSceneRoot {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub mod billboards;
|
|||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod explosions;
|
pub mod explosions;
|
||||||
pub mod observers;
|
pub mod observers;
|
||||||
|
pub mod run_conditions;
|
||||||
pub mod sprite_3d_animation;
|
pub mod sprite_3d_animation;
|
||||||
pub mod squish_animation;
|
pub mod squish_animation;
|
||||||
pub mod trail;
|
pub mod trail;
|
||||||
|
|||||||
5
crates/shared/src/utils/run_conditions.rs
Normal file
5
crates/shared/src/utils/run_conditions.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use bevy::ecs::{resource::Resource, system::Res};
|
||||||
|
|
||||||
|
pub fn resource_absent<R: Resource>(res: Option<Res<R>>) -> bool {
|
||||||
|
res.is_none()
|
||||||
|
}
|
||||||
4
justfile
4
justfile
@@ -5,8 +5,8 @@ tb_setup_mac:
|
|||||||
ln -s $(pwd)/trenchbroom/hedz/hedz.fgd "$HOME/Library/Application Support/TrenchBroom/games/hedz/hedz.fgd" | true
|
ln -s $(pwd)/trenchbroom/hedz/hedz.fgd "$HOME/Library/Application Support/TrenchBroom/games/hedz/hedz.fgd" | true
|
||||||
ln -s $(pwd)/trenchbroom/hedz/GameConfig.cfg "$HOME/Library/Application Support/TrenchBroom/games/hedz/GameConfig.cfg" | true
|
ln -s $(pwd)/trenchbroom/hedz/GameConfig.cfg "$HOME/Library/Application Support/TrenchBroom/games/hedz/GameConfig.cfg" | true
|
||||||
|
|
||||||
run:
|
run *args:
|
||||||
RUST_BACKTRACE=1 cargo r --bin hedz_reloaded
|
RUST_BACKTRACE=1 cargo r --bin hedz_reloaded -- {{args}}
|
||||||
|
|
||||||
server:
|
server:
|
||||||
RUST_BACKTRACE=1 cargo r --bin server
|
RUST_BACKTRACE=1 cargo r --bin server
|
||||||
|
|||||||
Reference in New Issue
Block a user