Back to Home

ESO Lua File v100034

libraries/zo_movementcontroller/zo_movementcontroller.lua

[◄ back to folders ]
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
ZO_MovementController = ZO_Object:Subclass()
--[[ Public API ]]--
function ZO_MovementController:New(...)
    local movementController = ZO_Object.New(self)
    movementController:Initialize(...)
    return movementController
end
MOVEMENT_CONTROLLER_DIRECTION_VERTICAL = 1
MOVEMENT_CONTROLLER_DIRECTION_HORIZONTAL = 2
local function GetStickMagnitude(direction)
    if direction == MOVEMENT_CONTROLLER_DIRECTION_VERTICAL then
        return DIRECTIONAL_INPUT:GetY(ZO_DI_LEFT_STICK, ZO_DI_DPAD)
    end
    return -DIRECTIONAL_INPUT:GetX(ZO_DI_LEFT_STICK, ZO_DI_DPAD)
end
local NUM_TICKS_TO_START_ACCELERATING = 5
local ACCELERATION_MAGNTITUDE_FACTOR = 3
local MAX_TICKS_TO_ACCEL_ACROSS = 30
function ZO_MovementController:Initialize(direction, accumulationPerSecondForChange, magnitudeQueryFunctionOverride)
    self.direction = direction or MOVEMENT_CONTROLLER_DIRECTION_VERTICAL
    self.accumulationPerSecondForChange = accumulationPerSecondForChange or 8
    self.magnitudeQueryFunction = magnitudeQueryFunctionOverride or GetStickMagnitude
    self.totalAccumulation = 0
    self.debt = 0
    self.lastMagnitude = 0
    self.allowAcceleration = true
    self.numTicksToStartAccelerating = NUM_TICKS_TO_START_ACCELERATING
    self.accelerationMagnitudeFactor = ACCELERATION_MAGNTITUDE_FACTOR
    self.numAccumulationTicks = 0
end
function ZO_MovementController:SetAllowAcceleration(allowAcceleration)
    self.allowAcceleration = allowAcceleration
end
function ZO_MovementController:SetAccumulationPerSecondForChange(accumulation)
    self.accumulationPerSecondForChange = accumulation
end
function ZO_MovementController:SetNumTicksToStartAccelerating(numTicks)
    self.numTicksToStartAccelerating = numTicks
end
function ZO_MovementController:SetAccelerationMagnitudeFactor(accelerationMagnitudeFactor)
    self.accelerationMagnitudeFactor = accelerationMagnitudeFactor
end
function ZO_MovementController:IsAtMaxVelocity()
    if self.numAccumulationTicks > MAX_TICKS_TO_ACCEL_ACROSS then
        return true --if we're over the MAX_TICKS_TO_ACCEL_ACROSS we definitely aren't accelerating anymore
    end
    if self.debt == 0 then --otherwise if a snapshot of the accumulation right now is greater than the accumulation per second required,
        local magnitude = self:GetMagnitude()--we're already updating every frame regardless of being at max velocity
        local snapshotAccumulation = magnitude * GetFrameDeltaNormalizedForTargetFramerate() * self:CalculateAccelerationFactor()
        if snapshotAccumulation >= self.accumulationPerSecondForChange then
            return true
        elseif snapshotAccumulation <= -self.accumulationPerSecondForChange then
            return true
        end
    end
    --for sufficiently large accumulation per second for change, you can potentially get to max velocity for a several frames before
    --numAcculmulationTicks > MAX_TICKS_TO_ACCEL_ACROSS, ie you might get MOVE, NO, NO, MOVE, NO, MOVE, NO, NO, MOVE, NO, but short of
    --keeping a running history of the last several frames we can't accurately predict that we've gotten to that point. Also 20 ticks
    --a second is way too many to require a move
    return false
end
MOVEMENT_CONTROLLER_NO_CHANGE = 0
MOVEMENT_CONTROLLER_MOVE_NEXT = 1
MOVEMENT_CONTROLLER_MOVE_PREVIOUS = 2
local function IsChangingDirections(previousMagnitude, newMagnitude)
    return (previousMagnitude > 0 and newMagnitude < 0)
        or (previousMagnitude < 0 and newMagnitude > 0)
end
local ACCUMULATION_DEBT_PERCENT_AFTER_FIRST_SELECT = 1
local MIN_MAGNITUDE_FOR_ACCEL = .8
function ZO_MovementController:CheckMovement()
    local magnitude = self:GetMagnitude()
    if magnitude == 0 then
        self.totalAccumulation = 0
        self.isMovingFromAccumulation = false
        self.lastMagnitude = 0
        self.numAccumulationTicks = 0
    else
        if IsChangingDirections(self.lastMagnitude, magnitude) then
            -- reset moving state when changing a direction
            self.isMovingFromAccumulation = false
            self.lastMagnitude = magnitude
            -- consume this input, some devices will "flip back" when the stick is tapped in a direction
            -- this will help, but not eliminate that effect
            return MOVEMENT_CONTROLLER_NO_CHANGE
        end
        if zo_abs(magnitude) < MIN_MAGNITUDE_FOR_ACCEL then
            self.numAccumulationTicks = 0
        end
        if self.isMovingFromAccumulation then
            local normalizedMagnitude = magnitude * GetFrameDeltaNormalizedForTargetFramerate()
            if self.debt > 0 then
                local absNormalizedMagnitude = zo_abs(normalizedMagnitude)
                -- Consume it all, nothing to do here
                if self.debt >= absNormalizedMagnitude then
                    self.debt = self.debt - absNormalizedMagnitude
                    return MOVEMENT_CONTROLLER_NO_CHANGE
                end
                normalizedMagnitude = normalizedMagnitude - self.debt * zo_sign(normalizedMagnitude)
                self.debt = 0
            end
            
            self.totalAccumulation = self.totalAccumulation + normalizedMagnitude * self:CalculateAccelerationFactor()
        else
            self.isMovingFromAccumulation = true
            self.numAccumulationTicks = 0
            self.totalAccumulation = magnitude > 0 and self.accumulationPerSecondForChange or -self.accumulationPerSecondForChange
            self.debt = zo_abs(self.totalAccumulation * ACCUMULATION_DEBT_PERCENT_AFTER_FIRST_SELECT)
        end
        self.lastMagnitude = magnitude
        if self.totalAccumulation >= self.accumulationPerSecondForChange then
            self.totalAccumulation = self.totalAccumulation - self.accumulationPerSecondForChange
            self.numAccumulationTicks = self.numAccumulationTicks + 1
            return MOVEMENT_CONTROLLER_MOVE_PREVIOUS
        elseif self.totalAccumulation <= -self.accumulationPerSecondForChange then
            self.totalAccumulation = self.totalAccumulation + self.accumulationPerSecondForChange
            self.numAccumulationTicks = self.numAccumulationTicks + 1
            return MOVEMENT_CONTROLLER_MOVE_NEXT
        end
    end
    return MOVEMENT_CONTROLLER_NO_CHANGE
end
--[[ Private API ]]--
function ZO_MovementController:GetMagnitude()
    return self.magnitudeQueryFunction(self.direction)
end
function ZO_MovementController:CalculateAccelerationFactor()
    if self.allowAcceleration then
        return 1.0 + ZO_EaseOutQuartic(zo_clampedPercentBetween(self.numTicksToStartAccelerating, MAX_TICKS_TO_ACCEL_ACROSS, self.numAccumulationTicks)) * self.accelerationMagnitudeFactor
    end
    return 1.0
end