veloren_common_systems/
tether.rs

1use 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/// This system is responsible for controlling mounts
14#[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                // Can't fail
107                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                        // TODO: proper moment of inertia
115                        * 500.0
116                        / follower_mass.0;
117                    // TODO: Should consider the offset
118                    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                        // TODO: proper moment of inertia
127                        * 500.0
128                        / leader_mass.0;
129                    // TODO: Should consider the offset
130                    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}