1 | --Settings-- |
2 | local ESP = { |
3 | Enabled = false, |
4 | Boxes = true, |
5 | BoxShift = CFrame.new(0,-1.5,0), |
6 | BoxSize = Vector3.new(4,6,0), |
7 | Color = Color3.fromRGB(255, 170, 0), |
8 | FaceCamera = false, |
9 | Names = true, |
10 | TeamColor = true, |
11 | Thickness = 2, |
12 | AttachShift = 1, |
13 | TeamMates = true, |
14 | Players = true, |
15 | |
16 | Objects = setmetatable({}, {__mode="kv"}), |
17 | Overrides = {} |
18 | } |
19 | |
20 | --Declarations-- |
21 | local cam = workspace.CurrentCamera |
22 | local plrs = game:GetService("Players") |
23 | local plr = plrs.LocalPlayer |
24 | local mouse = plr:GetMouse() |
25 | |
26 | local V3new = Vector3.new |
27 | local WorldToViewportPoint = cam.WorldToViewportPoint |
28 | |
29 | --Functions-- |
30 | local function Draw(obj, props) |
31 | local new = Drawing.new(obj) |
32 | |
33 | props = props or {} |
34 | for i,v in pairs(props) do |
35 | new[i] = v |
36 | end |
37 | return new |
38 | end |
39 | |
40 | function ESP:GetTeam(p) |
41 | local ov = self.Overrides.GetTeam |
42 | if ov then |
43 | return ov(p) |
44 | end |
45 | |
46 | return p and p.Team |
47 | end |
48 | |
49 | function ESP:IsTeamMate(p) |
50 | local ov = self.Overrides.IsTeamMate |
51 | if ov then |
52 | return ov(p) |
53 | end |
54 | |
55 | return self:GetTeam(p) == self:GetTeam(plr) |
56 | end |
57 | |
58 | function ESP:GetColor(obj) |
59 | local ov = self.Overrides.GetColor |
60 | if ov then |
61 | return ov(obj) |
62 | end |
63 | local p = self:GetPlrFromChar(obj) |
64 | return p and self.TeamColor and p.Team and p.Team.TeamColor.Color or self.Color |
65 | end |
66 | |
67 | function ESP:GetPlrFromChar(char) |
68 | local ov = self.Overrides.GetPlrFromChar |
69 | if ov then |
70 | return ov(char) |
71 | end |
72 | |
73 | return plrs:GetPlayerFromCharacter(char) |
74 | end |
75 | |
76 | function ESP:Toggle(bool) |
77 | self.Enabled = bool |
78 | if not bool then |
79 | for i,v in pairs(self.Objects) do |
80 | if v.Type == "Box" then --fov circle etc |
81 | if v.Temporary then |
82 | v:Remove() |
83 | else |
84 | for i,v in pairs(v.Components) do |
85 | v.Visible = false |
86 | end |
87 | end |
88 | end |
89 | end |
90 | end |
91 | end |
92 | |
93 | function ESP:GetBox(obj) |
94 | return self.Objects[obj] |
95 | end |
96 | |
97 | function ESP:AddObjectListener(parent, options) |
98 | local function NewListener(c) |
99 | if type(options.Type) == "string" and c:IsA(options.Type) or options.Type == nil then |
100 | if type(options.Name) == "string" and c.Name == options.Name or options.Name == nil then |
101 | if not options.Validator or options.Validator(c) then |
102 | local box = ESP:Add(c, { |
103 | PrimaryPart = type(options.PrimaryPart) == "string" and c:WaitForChild(options.PrimaryPart) or type(options.PrimaryPart) == "function" and options.PrimaryPart(c), |
104 | Color = type(options.Color) == "function" and options.Color(c) or options.Color, |
105 | ColorDynamic = options.ColorDynamic, |
106 | Name = type(options.CustomName) == "function" and options.CustomName(c) or options.CustomName, |
107 | IsEnabled = options.IsEnabled, |
108 | RenderInNil = options.RenderInNil |
109 | }) |
110 | --TODO: add a better way of passing options |
111 | if options.OnAdded then |
112 | coroutine.wrap(options.OnAdded)(box) |
113 | end |
114 | end |
115 | end |
116 | end |
117 | end |
118 | |
119 | if options.Recursive then |
120 | parent.DescendantAdded:Connect(NewListener) |
121 | for i,v in pairs(parent:GetDescendants()) do |
122 | coroutine.wrap(NewListener)(v) |
123 | end |
124 | else |
125 | parent.ChildAdded:Connect(NewListener) |
126 | for i,v in pairs(parent:GetChildren()) do |
127 | coroutine.wrap(NewListener)(v) |
128 | end |
129 | end |
130 | end |
131 | |
132 | local boxBase = {} |
133 | boxBase.__index = boxBase |
134 | |
135 | function boxBase:Remove() |
136 | ESP.Objects[self.Object] = nil |
137 | for i,v in pairs(self.Components) do |
138 | v.Visible = false |
139 | v:Remove() |
140 | self.Components[i] = nil |
141 | end |
142 | end |
143 | |
144 | function boxBase:Update() |
145 | if not self.PrimaryPart then |
146 | --warn("not supposed to print", self.Object) |
147 | return self:Remove() |
148 | end |
149 | |
150 | local color |
151 | if ESP.Highlighted == self.Object then |
152 | color = ESP.HighlightColor |
153 | else |
154 | color = self.Color or self.ColorDynamic and self:ColorDynamic() or ESP:GetColor(self.Object) or ESP.Color |
155 | end |
156 | |
157 | local allow = true |
158 | if ESP.Overrides.UpdateAllow and not ESP.Overrides.UpdateAllow(self) then |
159 | allow = false |
160 | end |
161 | if self.Player and not ESP.TeamMates and ESP:IsTeamMate(self.Player) then |
162 | allow = false |
163 | end |
164 | if self.Player and not ESP.Players then |
165 | allow = false |
166 | end |
167 | if self.IsEnabled and (type(self.IsEnabled) == "string" and not ESP[self.IsEnabled] or type(self.IsEnabled) == "function" and not self:IsEnabled()) then |
168 | allow = false |
169 | end |
170 | if not workspace:IsAncestorOf(self.PrimaryPart) and not self.RenderInNil then |
171 | allow = false |
172 | end |
173 | |
174 | if not allow then |
175 | for i,v in pairs(self.Components) do |
176 | v.Visible = false |
177 | end |
178 | return |
179 | end |
180 | |
181 | if ESP.Highlighted == self.Object then |
182 | color = ESP.HighlightColor |
183 | end |
184 | |
185 | --calculations-- |
186 | local cf = self.PrimaryPart.CFrame |
187 | if ESP.FaceCamera then |
188 | cf = CFrame.new(cf.p, cam.CFrame.p) |
189 | end |
190 | local size = self.Size |
191 | local locs = { |
192 | TopLeft = cf * ESP.BoxShift * CFrame.new(size.X/2,size.Y/2,0), |
193 | TopRight = cf * ESP.BoxShift * CFrame.new(-size.X/2,size.Y/2,0), |
194 | BottomLeft = cf * ESP.BoxShift * CFrame.new(size.X/2,-size.Y/2,0), |
195 | BottomRight = cf * ESP.BoxShift * CFrame.new(-size.X/2,-size.Y/2,0), |
196 | TagPos = cf * ESP.BoxShift * CFrame.new(0,size.Y/2,0), |
197 | Torso = cf * ESP.BoxShift |
198 | } |
199 | |
200 | if ESP.Boxes then |
201 | local TopLeft, Vis1 = WorldToViewportPoint(cam, locs.TopLeft.p) |
202 | local TopRight, Vis2 = WorldToViewportPoint(cam, locs.TopRight.p) |
203 | local BottomLeft, Vis3 = WorldToViewportPoint(cam, locs.BottomLeft.p) |
204 | local BottomRight, Vis4 = WorldToViewportPoint(cam, locs.BottomRight.p) |
205 | |
206 | if self.Components.Quad then |
207 | if Vis1 or Vis2 or Vis3 or Vis4 then |
208 | self.Components.Quad.Visible = true |
209 | self.Components.Quad.PointA = Vector2.new(TopRight.X, TopRight.Y) |
210 | self.Components.Quad.PointB = Vector2.new(TopLeft.X, TopLeft.Y) |
211 | self.Components.Quad.PointC = Vector2.new(BottomLeft.X, BottomLeft.Y) |
212 | self.Components.Quad.PointD = Vector2.new(BottomRight.X, BottomRight.Y) |
213 | self.Components.Quad.Color = color |
214 | else |
215 | self.Components.Quad.Visible = false |
216 | end |
217 | end |
218 | else |
219 | self.Components.Quad.Visible = false |
220 | end |
221 | |
222 | if ESP.Names then |
223 | local TagPos, Vis5 = WorldToViewportPoint(cam, locs.TagPos.p) |
224 | |
225 | if Vis5 then |
226 | self.Components.Name.Visible = true |
227 | self.Components.Name.Position = Vector2.new(TagPos.X, TagPos.Y) |
228 | self.Components.Name.Text = self.Name |
229 | self.Components.Name.Color = color |
230 | |
231 | self.Components.Distance.Visible = true |
232 | self.Components.Distance.Position = Vector2.new(TagPos.X, TagPos.Y + 14) |
233 | self.Components.Distance.Text = math.floor((cam.CFrame.p - cf.p).magnitude) .."m away" |
234 | self.Components.Distance.Color = color |
235 | else |
236 | self.Components.Name.Visible = false |
237 | self.Components.Distance.Visible = false |
238 | end |
239 | else |
240 | self.Components.Name.Visible = false |
241 | self.Components.Distance.Visible = false |
242 | end |
243 | |
244 | if ESP.Tracers then |
245 | local TorsoPos, Vis6 = WorldToViewportPoint(cam, locs.Torso.p) |
246 | |
247 | if Vis6 then |
248 | self.Components.Tracer.Visible = true |
249 | self.Components.Tracer.From = Vector2.new(TorsoPos.X, TorsoPos.Y) |
250 | self.Components.Tracer.To = Vector2.new(cam.ViewportSize.X/2,cam.ViewportSize.Y/ESP.AttachShift) |
251 | self.Components.Tracer.Color = color |
252 | else |
253 | self.Components.Tracer.Visible = false |
254 | end |
255 | else |
256 | self.Components.Tracer.Visible = false |
257 | end |
258 | end |
259 | |
260 | function ESP:Add(obj, options) |
261 | if not obj.Parent and not options.RenderInNil then |
262 | return warn(obj, "has no parent") |
263 | end |
264 | |
265 | local box = setmetatable({ |
266 | Name = options.Name or obj.Name, |
267 | Type = "Box", |
268 | Color = options.Color --[[or self:GetColor(obj)]], |
269 | Size = options.Size or self.BoxSize, |
270 | Object = obj, |
271 | Player = options.Player or plrs:GetPlayerFromCharacter(obj), |
272 | PrimaryPart = options.PrimaryPart or obj.ClassName == "Model" and (obj.PrimaryPart or obj:FindFirstChild("HumanoidRootPart") or obj:FindFirstChildWhichIsA("BasePart")) or obj:IsA("BasePart") and obj, |
273 | Components = {}, |
274 | IsEnabled = options.IsEnabled, |
275 | Temporary = options.Temporary, |
276 | ColorDynamic = options.ColorDynamic, |
277 | RenderInNil = options.RenderInNil |
278 | }, boxBase) |
279 | |
280 | if self:GetBox(obj) then |
281 | self:GetBox(obj):Remove() |
282 | end |
283 | |
284 | box.Components["Quad"] = Draw("Quad", { |
285 | Thickness = self.Thickness, |
286 | Color = color, |
287 | Transparency = 1, |
288 | Filled = false, |
289 | Visible = self.Enabled and self.Boxes |
290 | }) |
291 | box.Components["Name"] = Draw("Text", { |
292 | Text = box.Name, |
293 | Color = box.Color, |
294 | Center = true, |
295 | Outline = true, |
296 | Size = 19, |
297 | Visible = self.Enabled and self.Names |
298 | }) |
299 | box.Components["Distance"] = Draw("Text", { |
300 | Color = box.Color, |
301 | Center = true, |
302 | Outline = true, |
303 | Size = 19, |
304 | Visible = self.Enabled and self.Names |
305 | }) |
306 | |
307 | box.Components["Tracer"] = Draw("Line", { |
308 | Thickness = ESP.Thickness, |
309 | Color = box.Color, |
310 | Transparency = 1, |
311 | Visible = self.Enabled and self.Tracers |
312 | }) |
313 | self.Objects[obj] = box |
314 | |
315 | obj.AncestryChanged:Connect(function(_, parent) |
316 | if parent == nil and ESP.AutoRemove ~= false then |
317 | box:Remove() |
318 | end |
319 | end) |
320 | obj:GetPropertyChangedSignal("Parent"):Connect(function() |
321 | if obj.Parent == nil and ESP.AutoRemove ~= false then |
322 | box:Remove() |
323 | end |
324 | end) |
325 | |
326 | local hum = obj:FindFirstChildOfClass("Humanoid") |
327 | if hum then |
328 | hum.Died:Connect(function() |
329 | if ESP.AutoRemove ~= false then |
330 | box:Remove() |
331 | end |
332 | end) |
333 | end |
334 | |
335 | return box |
336 | end |
337 | |
338 | local function CharAdded(char) |
339 | local p = plrs:GetPlayerFromCharacter(char) |
340 | if not char:FindFirstChild("HumanoidRootPart") then |
341 | local ev |
342 | ev = char.ChildAdded:Connect(function(c) |
343 | if c.Name == "HumanoidRootPart" then |
344 | ev:Disconnect() |
345 | ESP:Add(char, { |
346 | Name = p.Name, |
347 | Player = p, |
348 | PrimaryPart = c |
349 | }) |
350 | end |
351 | end) |
352 | else |
353 | ESP:Add(char, { |
354 | Name = p.Name, |
355 | Player = p, |
356 | PrimaryPart = char.HumanoidRootPart |
357 | }) |
358 | end |
359 | end |
360 | local function PlayerAdded(p) |
361 | p.CharacterAdded:Connect(CharAdded) |
362 | if p.Character then |
363 | coroutine.wrap(CharAdded)(p.Character) |
364 | end |
365 | end |
366 | plrs.PlayerAdded:Connect(PlayerAdded) |
367 | for i,v in pairs(plrs:GetPlayers()) do |
368 | if v ~= plr then |
369 | PlayerAdded(v) |
370 | end |
371 | end |
372 | |
373 | game:GetService("RunService").RenderStepped:Connect(function() |
374 | cam = workspace.CurrentCamera |
375 | for i,v in (ESP.Enabled and pairs or ipairs)(ESP.Objects) do |
376 | if v.Update then |
377 | local s,e = pcall(v.Update, v) |
378 | if not s then warn("[EU]", e, v.Object:GetFullName()) end |
379 | end |
380 | end |
381 | end) |
382 | |
383 | return ESP |