-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhiders.lua
More file actions
185 lines (151 loc) · 5.96 KB
/
hiders.lua
File metadata and controls
185 lines (151 loc) · 5.96 KB
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
-- Configuration
local CONFIG = {
showBoxes = true,
showTracers = false,
boxColor = {255, 165, 0, 255}, -- Orange for hiders
tracerColor = {0, 0, 255, 255}, -- Blue for tracers
timeToMarkAsHider = 5.5, -- Seconds player must remain stationary
updatePositionThreshold = 1.0, -- Units player must move to be considered "moving"
maxDistance = 1650, -- Maximum distance to check for hiders (hammer units)
cleanupInterval = 1.0 -- Clean invalid players every second
}
-- Player tracking data
local playerData = {}
local lastLifeState = 2 -- Start with LIFE_DEAD (2)
local nextCleanupTime = 0
-- Pre-cache commonly used functions for performance
local floor = math.floor
local unpack = unpack or table.unpack
local RealTime = globals.RealTime
local WorldToScreen = client.WorldToScreen
local GetScreenSize = draw.GetScreenSize
local Color = draw.Color
local Line = draw.Line
local OutlinedRect = draw.OutlinedRect
-- Helper function to calculate distance between positions
local function DistanceBetweenVectors(v1, v2)
if not v1 or not v2 then return 0 end
local dx = v1.x - v2.x
local dy = v1.y - v2.y
local dz = v1.z - v2.z
return math.sqrt(dx * dx + dy * dy + dz * dz)
end
-- Reset all tracking data
local function ResetPlayerData()
playerData = {}
end
-- Clean up invalid targets
local function CleanInvalidTargets()
local currentTime = RealTime()
if currentTime < nextCleanupTime then return end
nextCleanupTime = currentTime + CONFIG.cleanupInterval
-- Only clean up if we have data to clean
if next(playerData) == nil then return end
local localPlayer = entities.GetLocalPlayer()
if not localPlayer then return end
for playerIndex, data in pairs(playerData) do
local entity = entities.GetByIndex(playerIndex)
-- Remove data only if entity is completely invalid or definitely not in play
if not entity or
not entity:IsAlive() or
entity:GetTeamNumber() == localPlayer:GetTeamNumber() then
playerData[playerIndex] = nil
end
end
end
-- Check if player is within valid range
local function IsPlayerInRange(player, localPlayer)
if not player or not localPlayer then return false end
local playerPos = player:GetAbsOrigin()
local localPos = localPlayer:GetAbsOrigin()
return DistanceBetweenVectors(playerPos, localPos) <= CONFIG.maxDistance
end
-- Update player data
local function UpdatePlayerData(player)
if not player or not player:IsAlive() then return end
local currentTime = RealTime()
local currentPos = player:GetAbsOrigin()
local playerIndex = player:GetIndex()
-- Initialize new player data
if not playerData[playerIndex] then
playerData[playerIndex] = {
lastPosition = currentPos,
lastMoveTime = currentTime,
isHider = false,
lastUpdateTime = currentTime
}
return
end
local data = playerData[playerIndex]
data.lastUpdateTime = currentTime
local distance = DistanceBetweenVectors(currentPos, data.lastPosition)
if distance > CONFIG.updatePositionThreshold then
data.lastPosition = currentPos
data.lastMoveTime = currentTime
data.isHider = false
elseif currentTime - data.lastMoveTime > CONFIG.timeToMarkAsHider then
data.isHider = true
end
end
local function DrawPlayerESP(player, data)
local playerPos = player:GetAbsOrigin()
local screenW, screenH = GetScreenSize()
local centerX, centerY = floor(screenW / 2), floor(screenH / 2)
-- Draw box
if CONFIG.showBoxes then
local mins = player:GetMins()
local maxs = player:GetMaxs()
if mins and maxs then
local bottomPos = Vector3(playerPos.x, playerPos.y, playerPos.z + mins.z)
local topPos = Vector3(playerPos.x, playerPos.y, playerPos.z + maxs.z)
local screenBottom = WorldToScreen(bottomPos)
local screenTop = WorldToScreen(topPos)
if screenBottom and screenTop then
local height = screenBottom[2] - screenTop[2]
local width = height * 0.75
local x1 = floor(screenBottom[1] - width / 2)
local y1 = floor(screenTop[2])
local x2 = floor(screenBottom[1] + width / 2)
local y2 = floor(screenBottom[2])
Color(unpack(CONFIG.boxColor))
OutlinedRect(x1, y1, x2, y2)
end
end
end
-- Draw tracer
if CONFIG.showTracers then
local screenPos = WorldToScreen(playerPos)
if screenPos then
Color(unpack(CONFIG.tracerColor))
Line(centerX, screenH, screenPos[1], screenPos[2])
end
end
end
local function OnDraw()
local localPlayer = entities.GetLocalPlayer()
if not localPlayer or not localPlayer:IsAlive() then return end
-- Check for respawn
local currentLifeState = localPlayer:GetPropInt("m_lifeState")
if lastLifeState == 2 and currentLifeState == 0 then -- If we went from dead to alive
ResetPlayerData()
end
lastLifeState = currentLifeState
-- Clean up invalid targets periodically
CleanInvalidTargets()
-- Process players
local players = entities.FindByClass("CTFPlayer")
for _, player in pairs(players) do
if player and player:IsAlive() and
player:GetTeamNumber() ~= localPlayer:GetTeamNumber() and
IsPlayerInRange(player, localPlayer) then
UpdatePlayerData(player)
local data = playerData[player:GetIndex()]
if data and data.isHider and not player:IsDormant() then
DrawPlayerESP(player, data)
end
end
end
end
-- Register callbacks
callbacks.Register("Draw", "HiderESP", OnDraw)
callbacks.Register("Unload", "HiderESPCleanup", ResetPlayerData)