veloren_common_systems/
tether.rs1use common::{
2 comp::{Body, Mass, Ori, Pos, Scale, Vel},
3 link::Is,
4 resources::DeltaTime,
5 tether::Follower,
6 uid::IdMaps,
7 util::Dir,
8};
9use common_ecs::{Job, Origin, Phase, System};
10use specs::{Entities, Join, LendJoin, Read, ReadStorage, WriteStorage};
11use vek::*;
12
13#[derive(Default)]
15pub struct Sys;
16impl<'a> System<'a> for Sys {
17 type SystemData = (
18 Read<'a, IdMaps>,
19 Entities<'a>,
20 Read<'a, DeltaTime>,
21 ReadStorage<'a, Is<Follower>>,
22 ReadStorage<'a, Pos>,
23 WriteStorage<'a, Vel>,
24 WriteStorage<'a, Ori>,
25 ReadStorage<'a, Body>,
26 ReadStorage<'a, Scale>,
27 ReadStorage<'a, Mass>,
28 );
29
30 const NAME: &'static str = "tether";
31 const ORIGIN: Origin = Origin::Common;
32 const PHASE: Phase = Phase::Create;
33
34 fn run(
35 _job: &mut Job<Self>,
36 (
37 id_maps,
38 entities,
39 dt,
40 is_followers,
41 positions,
42 mut velocities,
43 mut orientations,
44 bodies,
45 scales,
46 masses,
47 ): Self::SystemData,
48 ) {
49 for (follower, is_follower, follower_body, follower_scale) in
50 (&entities, &is_followers, bodies.maybe(), scales.maybe()).join()
51 {
52 let Some(leader) = id_maps.uid_entity(is_follower.leader) else {
53 continue;
54 };
55
56 let (Some(leader_pos), Some(follower_pos)) = (
57 positions.get(leader).copied(),
58 positions.get(follower).copied(),
59 ) else {
60 continue;
61 };
62
63 let (Some(leader_mass), Some(follower_mass)) =
64 (masses.get(leader).copied(), masses.get(follower).copied())
65 else {
66 continue;
67 };
68
69 if velocities.contains(follower) && velocities.contains(leader) {
70 let attach_offset = orientations
71 .get(leader)
72 .map(|ori| {
73 ori.to_quat()
74 * bodies
75 .get(leader)
76 .map(|b| {
77 b.tether_offset_leader()
78 * scales.get(leader).copied().unwrap_or(Scale(1.0)).0
79 })
80 .unwrap_or_default()
81 })
82 .unwrap_or_default();
83 let attach_pos = leader_pos.0 + attach_offset;
84
85 let tether_offset = orientations
86 .get(follower)
87 .map(|ori| {
88 ori.to_quat()
89 * follower_body
90 .map(|b| {
91 b.tether_offset_follower()
92 * follower_scale.copied().unwrap_or(Scale(1.0)).0
93 })
94 .unwrap_or_default()
95 })
96 .unwrap_or_default();
97 let tether_pos = follower_pos.0 + tether_offset;
98 let pull_factor =
99 (attach_pos.distance(tether_pos) - is_follower.tether_length).max(0.0);
100 let strength = pull_factor * 50000.0;
101 let pull_dir = (leader_pos.0 - follower_pos.0)
102 .try_normalized()
103 .unwrap_or(Vec3::unit_y());
104 let impulse = pull_dir * strength * dt.0;
105
106 velocities.get_mut(follower).unwrap().0 += impulse / follower_mass.0;
108 velocities.get_mut(leader).unwrap().0 -= impulse / leader_mass.0;
109
110 if let Some(follower_ori) = orientations.get_mut(follower) {
111 let turn_strength = pull_factor
112 * (tether_offset.magnitude() * tether_pos.distance(attach_pos)
113 - tether_offset.dot(attach_pos - tether_pos).abs())
114 * 500.0
116 / follower_mass.0;
117 let target_ori = follower_ori.yawed_towards(Dir::new(pull_dir));
119 *follower_ori = follower_ori.slerped_towards(target_ori, turn_strength * dt.0);
120 }
121
122 if let Some(leader_ori) = orientations.get_mut(leader) {
123 let turn_strength = pull_factor
124 * (attach_offset.magnitude() * tether_pos.distance(attach_pos)
125 - attach_offset.dot(tether_pos - attach_pos).abs())
126 * 500.0
128 / leader_mass.0;
129 let target_ori = leader_ori.yawed_towards(Dir::new(pull_dir));
131 *leader_ori = leader_ori.slerped_towards(target_ori, turn_strength * dt.0);
132 }
133 }
134 }
135 }
136}