1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::ecs::comp::Interpolated;
use common::{
    comp::{object, Body, Ori, Pos, Vel},
    resources::DeltaTime,
};
use common_ecs::{Job, Origin, Phase, System};
use specs::{Entities, Join, Read, ReadStorage, WriteStorage};
use tracing::warn;
use vek::*;

/// This system will allow NPCs to modify their controller
#[derive(Default)]
pub struct Sys;
impl<'a> System<'a> for Sys {
    type SystemData = (
        Entities<'a>,
        Read<'a, DeltaTime>,
        ReadStorage<'a, Pos>,
        ReadStorage<'a, Ori>,
        ReadStorage<'a, Vel>,
        ReadStorage<'a, Body>,
        WriteStorage<'a, Interpolated>,
    );

    const NAME: &'static str = "interpolation";
    const ORIGIN: Origin = Origin::Frontend("voxygen");
    const PHASE: Phase = Phase::Create;

    fn run(
        _job: &mut Job<Self>,
        (entities, dt, positions, orientations, velocities, bodies, mut interpolated): Self::SystemData,
    ) {
        // Update interpolated positions and orientations
        for (pos, ori, i, body, vel) in (
            &positions,
            &orientations,
            &mut interpolated,
            &bodies,
            &velocities,
        )
            .join()
        {
            // Update interpolation values, but don't interpolate far things or objects
            if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) {
                // Note, these values are specifically tuned for smoother motion with high
                // network latency or low network sampling rate and for smooth
                // block hopping (which is instantaneous)
                const POS_LERP_RATE_FACTOR: f32 = 10.0;
                i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, POS_LERP_RATE_FACTOR * dt.0);
                i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0);
            } else {
                i.pos = pos.0;
                i.ori = *ori;
            }
        }
        // Insert interpolation components for entities which don't have them
        for (entity, pos, ori) in (&entities, &positions, &orientations, !&interpolated)
            .join()
            .map(|(e, p, o, _)| (e, p.0, *o))
            .collect::<Vec<_>>()
        {
            interpolated
                .insert(entity, Interpolated { pos, ori })
                .err()
                .map(|e| warn!(?e, "Error inserting Interpolated component"));
        }
        // Remove Interpolated component from entities which don't have a position or an
        // orientation or a velocity
        for entity in (&entities, !&positions, &interpolated)
            .join()
            .map(|(e, _, _)| e)
            .collect::<Vec<_>>()
        {
            interpolated.remove(entity);
        }
        for entity in (&entities, !&orientations, &interpolated)
            .join()
            .map(|(e, _, _)| e)
            .collect::<Vec<_>>()
        {
            interpolated.remove(entity);
        }
        for entity in (&entities, !&velocities, &interpolated)
            .join()
            .map(|(e, _, _)| e)
            .collect::<Vec<_>>()
        {
            interpolated.remove(entity);
        }
    }
}

fn base_ori_interp(body: &Body) -> f32 {
    match body {
        Body::Object(
            object::Body::Crossbow
            | object::Body::Flamethrower
            | object::Body::Lavathrower
            | object::Body::HaniwaSentry
            | object::Body::TerracottaStatue
            | object::Body::MinotaurAxe,
        ) => 100.0,
        _ => 10.0,
    }
}