forked from JWalkerMailly/MetroidPrimeSWEP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhelmet.lua
202 lines (157 loc) · 6.96 KB
/
helmet.lua
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
function POWERSUIT:IsBoundingBoxVisible(entity, maxDistance)
local owner = self:GetOwner();
local eyePos = owner:EyePos();
local endPos = eyePos + (entity:GetLockOnPosition() - eyePos):GetNormalized() * (maxDistance + 50);
local trace = util.TraceHull({
start = eyePos,
endpos = endPos,
mins = WGL.OneVec * -2.5,
maxs = WGL.OneVec * 2.5,
filter = { self, owner },
mask = MASK_SHOT
});
return trace.Hit && entity == trace.Entity;
end
function POWERSUIT:GetDangerRatio()
local pos = self:GetOwner():GetPos();
local maxDistance = self.Helmet.Constants.Visor.DangerDistance;
local closestDanger = maxDistance;
-- Scan all potentially dangerous entities and keep the closest one for reference.
for k,v in pairs(ents.FindInSphere(pos, maxDistance)) do
if (game.MetroidPrimeThreats.Cache[v:GetClass()]) then
local distance = v:GetPos():Distance(pos);
if (distance < closestDanger) then closestDanger = distance; end
end
end
-- Return threat ratio to closest entity.
return WGL.Clamp(1 - math.Round(closestDanger / maxDistance, 3));
end
function POWERSUIT:GetAimVector(owner, shootPos, aimVector, target, autoTarget, lockedOn)
-- Auto targeting is a special feature used primarily for the wavebuster.
if (autoTarget) then return (target:GetLockOnPosition() - shootPos):GetNormalized(); end
local shouldAutoAim = tobool(owner:GetInfo("mp_options_autoaim")) || lockedOn;
-- Regular aim assist works by establishing a maximal aim angle vector and predicting a set number of frames in advance.
local autoAimVector = shouldAutoAim && ((target:GetLockOnPosition() + target:GetVelocity() * FrameTime() * self.Helmet.Constants.Visor.AimAssistFrames) - shootPos):GetNormalized() || aimVector;
if (autoAimVector:Dot(aimVector) > self.Helmet:GetAimAssistAngle()) then return autoAimVector; end
return aimVector;
end
function POWERSUIT:GetAimData(aimAssist, autoTarget)
-- Only process aim assist for valid targets. Grapple anchor points are considered valid only
-- for aim lock, not for shoot data. This prevents projectiles from homing grapple points.
local validTarget = false;
local shootPos, owner, aimVector = self:GetMuzzlePos();
local target, targetValid, lockedOn = self.Helmet:GetTarget(IN_SPEED);
if (targetValid) then
validTarget = !target:IsGrappleAnchor();
if (aimAssist && validTarget) then aimVector = self:GetAimVector(owner, shootPos, aimVector, target, autoTarget, lockedOn); end
end
return {
Owner = owner,
Weapon = self,
ShootPos = shootPos,
AimVector = aimVector,
Target = target,
ValidTarget = validTarget,
Locked = lockedOn,
Assist = aimAssist,
Auto = autoTarget
};
end
function POWERSUIT:CanBeLockedOn(visor, entity, maxDistance)
local owner = self:GetOwner();
if (!IsValid(entity) || entity == owner || entity == self) then return false, NULL, false; end
local targetPos = entity:GetLockOnPosition();
local isAnchor = entity:IsGrappleAnchor();
if (!visor.AllowLockAll) then
if (!isAnchor && !WGL.IsAlive(entity)) then return false, NULL, false; end
local isVisible = self:IsBoundingBoxVisible(entity, maxDistance);
if (!isVisible) then return false, NULL, false; end
if (isAnchor) then
local distanceLimit = self.PowerSuit.Constants.Grapple.MaxDistance;
local isWithinReach = targetPos:DistToSqr(owner:EyePos()) <= distanceLimit * distanceLimit;
if (!isWithinReach || !self.PowerSuit:IsGrappleEnabled()) then return false, NULL, false; end
end
else
-- Call to API for implementation homogenization.
local isScannable = entity:CanBeScanned();
if (!isScannable) then return false, NULL, false; end
local isVisible = self:IsBoundingBoxVisible(entity, maxDistance);
if (!isVisible) then return false, NULL, false; end
end
return true, targetPos, isAnchor;
end
function POWERSUIT:AcquireTarget(ply, maxDistance, maxCosine)
-- Do nothing if the player is preemptively holding down the lock on key.
if (!ply:KeyPressed(IN_SPEED) && ply:KeyDown(IN_SPEED)) then return nil, nil; end
local visor = self:GetVisor();
local eyePos = ply:EyePos();
local aimVector = ply:GetAimVector();
local viewVector = aimVector * maxDistance;
local lastDistance = maxDistance;
local currentTarget = NULL;
local isAnchor = false;
-- Scan every viable entity in view frustrum.
for k,v in pairs(ents.FindInCone(eyePos, aimVector, maxDistance, maxCosine)) do
local locked, pos, anchor = self:CanBeLockedOn(visor, v, maxDistance);
if (!locked) then continue; end
-- Compute the closest distance from the aim vector in order to determine
-- which entity is most suitable for aim lock.
local targetDistance = util.DistanceToLine(eyePos, eyePos + viewVector, pos);
if (targetDistance < lastDistance) then
currentTarget, isAnchor = v, anchor;
lastDistance = targetDistance;
end
end
return currentTarget, isAnchor;
end
function POWERSUIT:HelmetThink()
if (SERVER || (!game.SinglePlayer() && !IsFirstTimePredicted())) then return; end
-- Play lock on sound.
local target, _, lockedOn = self.Helmet:GetTarget(IN_SPEED);
if (lockedOn) then
if (!self.LockedOnSound) then
local isAnchor = target:IsGrappleAnchor();
WSL.PlaySoundPatch(self:GetVisor(), isAnchor && "grapple" || "aimlock", 0.3, 0);
self.LockedOnSound = true;
end
else
self.LockedOnSound = false;
end
-- Throttle helmet think.
if (self.NextHelmetThink || 0) > CurTime() then return; end
local owner = self:GetOwner();
if (!IsValid(owner) || owner:InVehicle()) then return; end
-- Do nothing if target didn't change.
local visor = self.Helmet.Constants.Visor;
local nextTarget = self:AcquireTarget(owner, visor.LockOnDistance, visor.LockOnCosine);
if (nextTarget == nil || target == nextTarget) then return; end
-- Propagate to server.
net.Start("POWERSUIT.SwitchTarget");
net.WriteEntity(self);
net.WriteEntity(nextTarget);
net.SendToServer();
hook.Run("MP.OnTargetChanged", self, nextTarget);
self.NextHelmetThink = CurTime() + visor.LockOnFPS;
end
-- Change target networking code.
if (SERVER) then util.AddNetworkString("POWERSUIT.SwitchTarget"); end
net.Receive("POWERSUIT.SwitchTarget", function(length, ply)
local powersuit = net.ReadEntity();
local target = net.ReadEntity();
-- Safety check in case the networked information is invalid.
if (!IsValid(powersuit) || powersuit:GetOwner() != ply) then return; end
-- Network target data.
if (target:IsWorld()) then target = NULL; end
powersuit.Helmet:SetTarget(target);
hook.Run("MP.OnTargetChanged", powersuit, target);
end);
-- Scan completed network code.
if (SERVER) then util.AddNetworkString("POWERSUIT.ScanCompleted"); end
net.Receive("POWERSUIT.ScanCompleted", function(length, ply)
local powersuit = net.ReadEntity();
local target = net.ReadEntity();
-- Safety check in case the networked information is invalid.
if (!IsValid(powersuit) || powersuit:GetOwner() != ply) then return; end
-- Raise event callback.
hook.Run("MP.OnScanCompleted", powersuit, target);
end)