qiell

Better radar

Aug 30th, 2026
12
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 44.33 KB | None | 0 0
  1. -- [[ DELTA RADAR V26: OPTIMIZED PLAYER/NPC RADAR ]] --
  2. -- Client-side LocalScript
  3. --
  4. -- V26 improvements:
  5. -- • Optimized radar update loop
  6. -- • 30 FPS radar refresh instead of Heartbeat workload
  7. -- • No xpcall inside the hot update loop
  8. -- • Cached character roots
  9. -- • Cached humanoids
  10. -- • Event-driven NPC detection
  11. -- • Lightweight NPC synchronization
  12. -- • Player/NPC mode toggle
  13. -- • Player respawn support
  14. -- • NPC spawn support
  15. -- • PlayerRemoving cleanup
  16. -- • Duplicate-dot prevention
  17. -- • Separate name/distance labels
  18. -- • Nearby-label collision handling
  19. -- • Labels hidden when excessively crowded
  20. -- • Horizontal distance only
  21. -- • Player-relative coordinates
  22. -- • Forward = radar top
  23. -- • Range validation
  24. -- • Radar resize
  25. -- • Dragging
  26. -- • Safe cleanup
  27. --
  28. -- Place this in a LocalScript.
  29.  
  30.  
  31. -----------------------------------------------------------
  32. -- SERVICES
  33. -----------------------------------------------------------
  34.  
  35. local Players = game:GetService("Players")
  36. local RunService = game:GetService("RunService")
  37. local UserInputService = game:GetService("UserInputService")
  38.  
  39.  
  40. -----------------------------------------------------------
  41. -- CONFIGURATION
  42. -----------------------------------------------------------
  43.  
  44. local RADAR_WIDTH = 130
  45. local RADAR_HEIGHT = 80
  46.  
  47. local MAX_DISTANCE = 350
  48.  
  49. local DOT_SIZE = 6
  50.  
  51. -- Radar update rate.
  52. -- 30 updates per second is enough for a radar while
  53. -- considerably reducing GUI/property workload.
  54. local RADAR_UPDATE_RATE = 1 / 30
  55.  
  56. -- Safety synchronization.
  57. local SAFETY_SCAN_RATE = 5
  58.  
  59. local MIN_RADAR_SIZE = 80
  60. local MAX_RADAR_SIZE = 300
  61.  
  62. local MIN_RANGE = 10
  63. local MAX_RANGE = 10000
  64.  
  65. -- Label collision settings.
  66. local LABEL_COLLISION_DISTANCE = 18
  67. local LABEL_OFFSET = 9
  68.  
  69. -- Maximum number of labels that can be displayed in a
  70. -- very crowded local area.
  71. local MAX_LABELS_PER_CLUSTER = 3
  72.  
  73.  
  74. -----------------------------------------------------------
  75. -- PLAYER
  76. -----------------------------------------------------------
  77.  
  78. local player = Players.LocalPlayer
  79. local playerGui = player:WaitForChild("PlayerGui")
  80.  
  81.  
  82. -----------------------------------------------------------
  83. -- STATE
  84. -----------------------------------------------------------
  85.  
  86. local activeDots = {}
  87.  
  88. local playerConnections = {}
  89. local connections = {}
  90.  
  91. local destroyed = false
  92.  
  93. local dragging = false
  94. local dragStart = nil
  95. local startPos = nil
  96.  
  97. local currentMode = "Players"
  98.  
  99. local radarAccumulator = 0
  100.  
  101. local localRoot = nil
  102.  
  103. -- Cache target information.
  104. local targetCache = {}
  105.  
  106.  
  107. -----------------------------------------------------------
  108. -- SAFE CALL
  109. -----------------------------------------------------------
  110.  
  111. local function safeCall(label, callback, ...)
  112. local success, result = xpcall(
  113. callback,
  114. function(errorMessage)
  115. return debug.traceback(
  116. "[" .. tostring(label) .. "] "
  117. .. tostring(errorMessage),
  118. 2
  119. )
  120. end,
  121. ...
  122. )
  123.  
  124. if not success then
  125. warn(
  126. "[Delta Radar V26] "
  127. .. tostring(result)
  128. )
  129.  
  130. return false, nil
  131. end
  132.  
  133. return true, result
  134. end
  135.  
  136.  
  137. -----------------------------------------------------------
  138. -- CLEANUP OLD RADARS
  139. -----------------------------------------------------------
  140.  
  141. for _, gui in ipairs(playerGui:GetChildren()) do
  142.  
  143. if gui:IsA("ScreenGui")
  144. and gui.Name:find(
  145. "Delta_Radar",
  146. 1,
  147. true
  148. ) then
  149.  
  150. pcall(function()
  151. gui:Destroy()
  152. end)
  153. end
  154. end
  155.  
  156.  
  157. -----------------------------------------------------------
  158. -- SCREEN GUI
  159. -----------------------------------------------------------
  160.  
  161. local screenGui = Instance.new("ScreenGui")
  162.  
  163. screenGui.Name =
  164. "Delta_Radar_V26_Optimized"
  165.  
  166. screenGui.ResetOnSpawn = false
  167. screenGui.IgnoreGuiInset = true
  168.  
  169. screenGui.Parent = playerGui
  170.  
  171.  
  172. -----------------------------------------------------------
  173. -- MAIN CONTAINER
  174. -----------------------------------------------------------
  175.  
  176. local mainContainer = Instance.new("Frame")
  177.  
  178. mainContainer.Name =
  179. "MainContainer"
  180.  
  181. mainContainer.Size =
  182. UDim2.new(
  183. 0,
  184. RADAR_WIDTH,
  185. 0,
  186. RADAR_HEIGHT + 105
  187. )
  188.  
  189. mainContainer.Position =
  190. UDim2.new(
  191. 0.5,
  192. -RADAR_WIDTH / 2,
  193. 0.05,
  194. 0
  195. )
  196.  
  197. mainContainer.BackgroundTransparency = 1
  198. mainContainer.Active = true
  199.  
  200. mainContainer.Parent = screenGui
  201.  
  202.  
  203. -----------------------------------------------------------
  204. -- MODE BUTTON
  205. -----------------------------------------------------------
  206.  
  207. local modeButton = Instance.new("TextButton")
  208.  
  209. modeButton.Name =
  210. "ModeButton"
  211.  
  212. modeButton.Size =
  213. UDim2.new(
  214. 0,
  215. RADAR_WIDTH,
  216. 0,
  217. 28
  218. )
  219.  
  220. modeButton.Position =
  221. UDim2.new(
  222. 0,
  223. 0,
  224. 0,
  225. 0
  226. )
  227.  
  228. modeButton.BackgroundColor3 =
  229. Color3.fromRGB(
  230. 5,
  231. 5,
  232. 10
  233. )
  234.  
  235. modeButton.BackgroundTransparency = 0.15
  236.  
  237. modeButton.TextColor3 =
  238. Color3.fromRGB(
  239. 0,
  240. 255,
  241. 0
  242. )
  243.  
  244. modeButton.TextSize = 12
  245. modeButton.Font = Enum.Font.Code
  246.  
  247. modeButton.Text =
  248. "MODE: PLAYERS"
  249.  
  250. modeButton.AutoButtonColor = true
  251.  
  252. modeButton.ZIndex = 30
  253.  
  254. modeButton.Parent = mainContainer
  255.  
  256.  
  257. local modeCorner =
  258. Instance.new("UICorner")
  259.  
  260. modeCorner.CornerRadius =
  261. UDim.new(
  262. 0,
  263. 6
  264. )
  265.  
  266. modeCorner.Parent = modeButton
  267.  
  268.  
  269. local modeStroke =
  270. Instance.new("UIStroke")
  271.  
  272. modeStroke.Thickness = 1.5
  273.  
  274. modeStroke.Color =
  275. Color3.fromRGB(
  276. 0,
  277. 80,
  278. 200
  279. )
  280.  
  281. modeStroke.ApplyStrokeMode =
  282. Enum.ApplyStrokeMode.Border
  283.  
  284. modeStroke.Parent = modeButton
  285.  
  286.  
  287. -----------------------------------------------------------
  288. -- RANGE LABEL
  289. -----------------------------------------------------------
  290.  
  291. local rangeLabel = Instance.new("TextLabel")
  292.  
  293. rangeLabel.Name =
  294. "RangeLabel"
  295.  
  296. rangeLabel.Size =
  297. UDim2.new(
  298. 0,
  299. 45,
  300. 0,
  301. 25
  302. )
  303.  
  304. rangeLabel.Position =
  305. UDim2.new(
  306. 0,
  307. 0,
  308. 0,
  309. 35
  310. )
  311.  
  312. rangeLabel.BackgroundTransparency = 1
  313.  
  314. rangeLabel.Text =
  315. "Range"
  316.  
  317. rangeLabel.TextColor3 =
  318. Color3.fromRGB(
  319. 255,
  320. 255,
  321. 255
  322. )
  323.  
  324. rangeLabel.TextSize = 12
  325. rangeLabel.Font = Enum.Font.Code
  326.  
  327. rangeLabel.TextXAlignment =
  328. Enum.TextXAlignment.Right
  329.  
  330. rangeLabel.Parent = mainContainer
  331.  
  332.  
  333. -----------------------------------------------------------
  334. -- RANGE BOX
  335. -----------------------------------------------------------
  336.  
  337. local rangeBox = Instance.new("TextBox")
  338.  
  339. rangeBox.Name =
  340. "RangeBox"
  341.  
  342. rangeBox.Size =
  343. UDim2.new(
  344. 0,
  345. 80,
  346. 0,
  347. 25
  348. )
  349.  
  350. rangeBox.Position =
  351. UDim2.new(
  352. 0.5,
  353. -20,
  354. 0,
  355. 35
  356. )
  357.  
  358. rangeBox.BackgroundColor3 =
  359. Color3.fromRGB(
  360. 5,
  361. 5,
  362. 10
  363. )
  364.  
  365. rangeBox.BackgroundTransparency = 0.2
  366.  
  367. rangeBox.TextColor3 =
  368. Color3.fromRGB(
  369. 0,
  370. 255,
  371. 0
  372. )
  373.  
  374. rangeBox.Font = Enum.Font.Code
  375. rangeBox.TextSize = 14
  376.  
  377. rangeBox.Text =
  378. tostring(MAX_DISTANCE)
  379.  
  380. rangeBox.ClearTextOnFocus = false
  381.  
  382. rangeBox.Parent = mainContainer
  383.  
  384.  
  385. local rangeCorner =
  386. Instance.new("UICorner")
  387.  
  388. rangeCorner.CornerRadius =
  389. UDim.new(
  390. 0,
  391. 6
  392. )
  393.  
  394. rangeCorner.Parent = rangeBox
  395.  
  396.  
  397. local rangeStroke =
  398. Instance.new("UIStroke")
  399.  
  400. rangeStroke.Thickness = 1.5
  401.  
  402. rangeStroke.Color =
  403. Color3.fromRGB(
  404. 0,
  405. 80,
  406. 200
  407. )
  408.  
  409. rangeStroke.ApplyStrokeMode =
  410. Enum.ApplyStrokeMode.Border
  411.  
  412. rangeStroke.Parent = rangeBox
  413.  
  414.  
  415. -----------------------------------------------------------
  416. -- RADAR FRAME
  417. -----------------------------------------------------------
  418.  
  419. local radarFrame = Instance.new("Frame")
  420.  
  421. radarFrame.Name =
  422. "RadarFrame"
  423.  
  424. radarFrame.Size =
  425. UDim2.new(
  426. 0,
  427. RADAR_WIDTH,
  428. 0,
  429. RADAR_HEIGHT
  430. )
  431.  
  432. radarFrame.Position =
  433. UDim2.new(
  434. 0,
  435. 0,
  436. 0,
  437. 65
  438. )
  439.  
  440. radarFrame.BackgroundColor3 =
  441. Color3.fromRGB(
  442. 5,
  443. 5,
  444. 10
  445. )
  446.  
  447. radarFrame.BackgroundTransparency = 0.2
  448.  
  449. radarFrame.ClipsDescendants = true
  450.  
  451. radarFrame.Parent = mainContainer
  452.  
  453.  
  454. local radarCorner =
  455. Instance.new("UICorner")
  456.  
  457. radarCorner.CornerRadius =
  458. UDim.new(
  459. 0,
  460. 8
  461. )
  462.  
  463. radarCorner.Parent = radarFrame
  464.  
  465.  
  466. local radarStroke =
  467. Instance.new("UIStroke")
  468.  
  469. radarStroke.Thickness = 2
  470.  
  471. radarStroke.Color =
  472. Color3.fromRGB(
  473. 0,
  474. 80,
  475. 200
  476. )
  477.  
  478. radarStroke.ApplyStrokeMode =
  479. Enum.ApplyStrokeMode.Border
  480.  
  481. radarStroke.Parent = radarFrame
  482.  
  483.  
  484. -----------------------------------------------------------
  485. -- CENTER POINTER
  486. -----------------------------------------------------------
  487.  
  488. local centerDot =
  489. Instance.new("Frame")
  490.  
  491. centerDot.Name =
  492. "CenterPointer"
  493.  
  494. centerDot.Size =
  495. UDim2.new(
  496. 0,
  497. 6,
  498. 0,
  499. 6
  500. )
  501.  
  502. centerDot.Position =
  503. UDim2.new(
  504. 0.5,
  505. -3,
  506. 0.5,
  507. -3
  508. )
  509.  
  510. centerDot.BackgroundTransparency = 1
  511.  
  512. centerDot.ZIndex = 20
  513.  
  514. centerDot.Parent = radarFrame
  515.  
  516.  
  517. -----------------------------------------------------------
  518. -- DIRECTION ARROW
  519. -----------------------------------------------------------
  520.  
  521. local directionArrow =
  522. Instance.new("TextLabel")
  523.  
  524. directionArrow.Name =
  525. "DirectionArrow"
  526.  
  527. directionArrow.Size =
  528. UDim2.new(
  529. 1,
  530. 0,
  531. 1,
  532. 0
  533. )
  534.  
  535. directionArrow.BackgroundTransparency = 1
  536.  
  537. directionArrow.Text =
  538. "▲"
  539.  
  540. directionArrow.TextColor3 =
  541. Color3.fromRGB(
  542. 0,
  543. 255,
  544. 0
  545. )
  546.  
  547. directionArrow.TextSize = 8
  548.  
  549. directionArrow.ZIndex = 21
  550.  
  551. directionArrow.Parent = centerDot
  552.  
  553.  
  554. local arrowStroke =
  555. Instance.new("UIStroke")
  556.  
  557. arrowStroke.Thickness = 1
  558.  
  559. arrowStroke.Color =
  560. Color3.new(
  561. 0,
  562. 0,
  563. 0
  564. )
  565.  
  566. arrowStroke.Parent = directionArrow
  567.  
  568.  
  569. -----------------------------------------------------------
  570. -- SIZE LABEL
  571. -----------------------------------------------------------
  572.  
  573. local sizeLabel =
  574. Instance.new("TextLabel")
  575.  
  576. sizeLabel.Name =
  577. "SizeLabel"
  578.  
  579. sizeLabel.Size =
  580. UDim2.new(
  581. 0,
  582. 65,
  583. 0,
  584. 25
  585. )
  586.  
  587. sizeLabel.Position =
  588. UDim2.new(
  589. 0,
  590. 0,
  591. 0,
  592. RADAR_HEIGHT + 70
  593. )
  594.  
  595. sizeLabel.BackgroundTransparency = 1
  596.  
  597. sizeLabel.Text =
  598. "Radar Size"
  599.  
  600. sizeLabel.TextColor3 =
  601. Color3.fromRGB(
  602. 255,
  603. 255,
  604. 255
  605. )
  606.  
  607. sizeLabel.TextSize = 11
  608. sizeLabel.Font = Enum.Font.Code
  609.  
  610. sizeLabel.TextXAlignment =
  611. Enum.TextXAlignment.Right
  612.  
  613. sizeLabel.Parent = mainContainer
  614.  
  615.  
  616. -----------------------------------------------------------
  617. -- SIZE BOX
  618. -----------------------------------------------------------
  619.  
  620. local sizeBox =
  621. Instance.new("TextBox")
  622.  
  623. sizeBox.Name =
  624. "SizeBox"
  625.  
  626. sizeBox.Size =
  627. UDim2.new(
  628. 0,
  629. 70,
  630. 0,
  631. 25
  632. )
  633.  
  634. sizeBox.Position =
  635. UDim2.new(
  636. 0.5,
  637. -5,
  638. 0,
  639. RADAR_HEIGHT + 70
  640. )
  641.  
  642. sizeBox.BackgroundColor3 =
  643. Color3.fromRGB(
  644. 5,
  645. 5,
  646. 10
  647. )
  648.  
  649. sizeBox.BackgroundTransparency = 0.2
  650.  
  651. sizeBox.TextColor3 =
  652. Color3.fromRGB(
  653. 0,
  654. 255,
  655. 0
  656. )
  657.  
  658. sizeBox.Font = Enum.Font.Code
  659. sizeBox.TextSize = 14
  660.  
  661. sizeBox.Text =
  662. tostring(RADAR_WIDTH)
  663.  
  664. sizeBox.ClearTextOnFocus = false
  665.  
  666. sizeBox.Parent = mainContainer
  667.  
  668.  
  669. local sizeCorner =
  670. Instance.new("UICorner")
  671.  
  672. sizeCorner.CornerRadius =
  673. UDim.new(
  674. 0,
  675. 6
  676. )
  677.  
  678. sizeCorner.Parent = sizeBox
  679.  
  680.  
  681. local sizeStroke =
  682. Instance.new("UIStroke")
  683.  
  684. sizeStroke.Thickness = 1.5
  685.  
  686. sizeStroke.Color =
  687. Color3.fromRGB(
  688. 0,
  689. 80,
  690. 200
  691. )
  692.  
  693. sizeStroke.ApplyStrokeMode =
  694. Enum.ApplyStrokeMode.Border
  695.  
  696. sizeStroke.Parent = sizeBox
  697.  
  698.  
  699. -----------------------------------------------------------
  700. -- CHARACTER ROOT
  701. -----------------------------------------------------------
  702.  
  703. local function getCharacterRoot(character)
  704.  
  705. if not character
  706. or not character:IsA("Model") then
  707.  
  708. return nil
  709. end
  710.  
  711. local root =
  712. character:FindFirstChild(
  713. "HumanoidRootPart"
  714. )
  715.  
  716. if root
  717. and root:IsA("BasePart") then
  718.  
  719. return root
  720. end
  721.  
  722. local primary =
  723. character.PrimaryPart
  724.  
  725. if primary
  726. and primary:IsA("BasePart") then
  727.  
  728. return primary
  729. end
  730.  
  731. return nil
  732. end
  733.  
  734.  
  735. -----------------------------------------------------------
  736. -- PLAYER VALIDATION
  737. -----------------------------------------------------------
  738.  
  739. local function isTrackablePlayer(targetPlayer)
  740.  
  741. if not targetPlayer
  742. or not targetPlayer:IsA("Player") then
  743.  
  744. return false
  745. end
  746.  
  747. if targetPlayer == player then
  748. return false
  749. end
  750.  
  751. if not targetPlayer.Parent then
  752. return false
  753. end
  754.  
  755. local character =
  756. targetPlayer.Character
  757.  
  758. if not character
  759. or not character.Parent then
  760.  
  761. return false
  762. end
  763.  
  764. local humanoid =
  765. character:FindFirstChildOfClass(
  766. "Humanoid"
  767. )
  768.  
  769. if not humanoid
  770. or humanoid.Health <= 0 then
  771.  
  772. return false
  773. end
  774.  
  775. return getCharacterRoot(
  776. character
  777. ) ~= nil
  778. end
  779.  
  780.  
  781. -----------------------------------------------------------
  782. -- NPC ROOT
  783. -----------------------------------------------------------
  784.  
  785. local function getNPCRoot(model)
  786.  
  787. if not model
  788. or not model:IsA("Model") then
  789.  
  790. return nil
  791. end
  792.  
  793. local root =
  794. model:FindFirstChild(
  795. "HumanoidRootPart"
  796. )
  797.  
  798. if root
  799. and root:IsA("BasePart") then
  800.  
  801. return root
  802. end
  803.  
  804. local primary =
  805. model.PrimaryPart
  806.  
  807. if primary
  808. and primary:IsA("BasePart") then
  809.  
  810. return primary
  811. end
  812.  
  813. return nil
  814. end
  815.  
  816.  
  817. -----------------------------------------------------------
  818. -- NPC VALIDATION
  819. -----------------------------------------------------------
  820.  
  821. local function isTrackableNPC(model)
  822.  
  823. if not model
  824. or not model:IsA("Model") then
  825.  
  826. return false
  827. end
  828.  
  829. if not model.Parent then
  830. return false
  831. end
  832.  
  833. if Players:GetPlayerFromCharacter(model) then
  834. return false
  835. end
  836.  
  837. local humanoid =
  838. model:FindFirstChildOfClass(
  839. "Humanoid"
  840. )
  841.  
  842. if not humanoid
  843. or humanoid.Health <= 0 then
  844.  
  845. return false
  846. end
  847.  
  848. local root =
  849. getNPCRoot(model)
  850.  
  851. if not root then
  852. return false
  853. end
  854.  
  855. return not root.Anchored
  856. end
  857.  
  858.  
  859. -----------------------------------------------------------
  860. -- TARGET NAME
  861. -----------------------------------------------------------
  862.  
  863. local function getTargetName(target)
  864.  
  865. if currentMode == "Players" then
  866.  
  867. if target
  868. and target:IsA("Player") then
  869.  
  870. local displayName =
  871. target.DisplayName
  872.  
  873. local username =
  874. target.Name
  875.  
  876. if typeof(displayName) ~= "string"
  877. or displayName == "" then
  878.  
  879. return "@" .. username
  880. end
  881.  
  882. if displayName == username then
  883. return "@" .. username
  884. end
  885.  
  886. return displayName
  887. .. " @"
  888. .. username
  889. end
  890.  
  891. elseif currentMode == "NPC" then
  892.  
  893. if target
  894. and target:IsA("Model") then
  895.  
  896. return target.Name
  897. end
  898. end
  899.  
  900. return "Unknown"
  901. end
  902.  
  903.  
  904. -----------------------------------------------------------
  905. -- REMOVE DOT
  906. -----------------------------------------------------------
  907.  
  908. local function removeDot(target)
  909.  
  910. local data =
  911. activeDots[target]
  912.  
  913. if not data then
  914. return
  915. end
  916.  
  917. activeDots[target] = nil
  918. targetCache[target] = nil
  919.  
  920. if data.Dot then
  921. pcall(function()
  922. data.Dot:Destroy()
  923. end)
  924. end
  925. end
  926.  
  927.  
  928. -----------------------------------------------------------
  929. -- REMOVE ALL DOTS
  930. -----------------------------------------------------------
  931.  
  932. local function removeAllDots()
  933.  
  934. local targets = {}
  935.  
  936. for target in pairs(activeDots) do
  937. targets[#targets + 1] = target
  938. end
  939.  
  940. for _, target in ipairs(targets) do
  941. removeDot(target)
  942. end
  943. end
  944.  
  945.  
  946. -----------------------------------------------------------
  947. -- CREATE DOT
  948. -----------------------------------------------------------
  949.  
  950. local function createDot(target)
  951.  
  952. if destroyed
  953. or not target
  954. or activeDots[target] then
  955.  
  956. return
  957. end
  958.  
  959. local valid
  960.  
  961. if currentMode == "Players" then
  962. valid = isTrackablePlayer(target)
  963. else
  964. valid = isTrackableNPC(target)
  965. end
  966.  
  967. if not valid then
  968. return
  969. end
  970.  
  971. -------------------------------------------------------
  972. -- DOT
  973. -------------------------------------------------------
  974.  
  975. local dot =
  976. Instance.new("Frame")
  977.  
  978. dot.Name =
  979. "RadarDot"
  980.  
  981. dot.Size =
  982. UDim2.new(
  983. 0,
  984. DOT_SIZE,
  985. 0,
  986. DOT_SIZE
  987. )
  988.  
  989. if currentMode == "Players" then
  990.  
  991. dot.BackgroundColor3 =
  992. Color3.fromRGB(
  993. 255,
  994. 60,
  995. 60
  996. )
  997.  
  998. else
  999.  
  1000. dot.BackgroundColor3 =
  1001. Color3.fromRGB(
  1002. 255,
  1003. 170,
  1004. 40
  1005. )
  1006. end
  1007.  
  1008. dot.BorderSizePixel = 0
  1009. dot.ZIndex = 10
  1010. dot.Visible = false
  1011.  
  1012. dot.Parent = radarFrame
  1013.  
  1014.  
  1015. local dotCorner =
  1016. Instance.new("UICorner")
  1017.  
  1018. dotCorner.CornerRadius =
  1019. UDim.new(
  1020. 1,
  1021. 0
  1022. )
  1023.  
  1024. dotCorner.Parent = dot
  1025.  
  1026.  
  1027. -------------------------------------------------------
  1028. -- NAME LABEL
  1029. -------------------------------------------------------
  1030.  
  1031. local nameLabel =
  1032. Instance.new("TextLabel")
  1033.  
  1034. nameLabel.Name =
  1035. "TargetName"
  1036.  
  1037. nameLabel.AnchorPoint =
  1038. Vector2.new(
  1039. 0.5,
  1040. 1
  1041. )
  1042.  
  1043. nameLabel.Size =
  1044. UDim2.new(
  1045. 0,
  1046. 110,
  1047. 0,
  1048. 14
  1049. )
  1050.  
  1051. nameLabel.Position =
  1052. UDim2.new(
  1053. 0.5,
  1054. 0,
  1055. 0,
  1056. -3
  1057. )
  1058.  
  1059. nameLabel.BackgroundTransparency = 1
  1060.  
  1061. nameLabel.Text =
  1062. getTargetName(target)
  1063.  
  1064. nameLabel.TextColor3 =
  1065. Color3.fromRGB(
  1066. 255,
  1067. 255,
  1068. 255
  1069. )
  1070.  
  1071. nameLabel.TextSize = 8
  1072.  
  1073. nameLabel.Font =
  1074. Enum.Font.Code
  1075.  
  1076. nameLabel.TextXAlignment =
  1077. Enum.TextXAlignment.Center
  1078.  
  1079. nameLabel.TextTruncate =
  1080. Enum.TextTruncate.AtEnd
  1081.  
  1082. nameLabel.ZIndex = 12
  1083.  
  1084. nameLabel.Parent = dot
  1085.  
  1086.  
  1087. local nameStroke =
  1088. Instance.new("UIStroke")
  1089.  
  1090. nameStroke.Thickness = 1
  1091.  
  1092. nameStroke.Color =
  1093. Color3.new(
  1094. 0,
  1095. 0,
  1096. 0
  1097. )
  1098.  
  1099. nameStroke.Parent = nameLabel
  1100.  
  1101.  
  1102. -------------------------------------------------------
  1103. -- DISTANCE LABEL
  1104. -------------------------------------------------------
  1105.  
  1106. local distanceLabel =
  1107. Instance.new("TextLabel")
  1108.  
  1109. distanceLabel.Name =
  1110. "Distance"
  1111.  
  1112. distanceLabel.AnchorPoint =
  1113. Vector2.new(
  1114. 0.5,
  1115. 0
  1116. )
  1117.  
  1118. distanceLabel.Size =
  1119. UDim2.new(
  1120. 0,
  1121. 45,
  1122. 0,
  1123. 12
  1124. )
  1125.  
  1126. distanceLabel.Position =
  1127. UDim2.new(
  1128. 0.5,
  1129. 0,
  1130. 1,
  1131. 3
  1132. )
  1133.  
  1134. distanceLabel.BackgroundTransparency = 1
  1135.  
  1136. distanceLabel.TextColor3 =
  1137. Color3.fromRGB(
  1138. 190,
  1139. 255,
  1140. 190
  1141. )
  1142.  
  1143. distanceLabel.TextSize = 8
  1144.  
  1145. distanceLabel.Font =
  1146. Enum.Font.Code
  1147.  
  1148. distanceLabel.TextXAlignment =
  1149. Enum.TextXAlignment.Center
  1150.  
  1151. distanceLabel.ZIndex = 12
  1152.  
  1153. distanceLabel.Parent = dot
  1154.  
  1155.  
  1156. local distanceStroke =
  1157. Instance.new("UIStroke")
  1158.  
  1159. distanceStroke.Thickness = 1
  1160.  
  1161. distanceStroke.Color =
  1162. Color3.new(
  1163. 0,
  1164. 0,
  1165. 0
  1166. )
  1167.  
  1168. distanceStroke.Parent =
  1169. distanceLabel
  1170.  
  1171.  
  1172. -------------------------------------------------------
  1173. -- CACHE
  1174. -------------------------------------------------------
  1175.  
  1176. local root
  1177.  
  1178. local humanoid
  1179.  
  1180. if currentMode == "Players" then
  1181.  
  1182. local character =
  1183. target.Character
  1184.  
  1185. if character then
  1186. root =
  1187. getCharacterRoot(character)
  1188.  
  1189. humanoid =
  1190. character:FindFirstChildOfClass(
  1191. "Humanoid"
  1192. )
  1193. end
  1194.  
  1195. else
  1196.  
  1197. root =
  1198. getNPCRoot(target)
  1199.  
  1200. humanoid =
  1201. target:FindFirstChildOfClass(
  1202. "Humanoid"
  1203. )
  1204. end
  1205.  
  1206. activeDots[target] = {
  1207. Dot = dot,
  1208. NameLabel = nameLabel,
  1209. DistanceLabel = distanceLabel,
  1210. }
  1211.  
  1212. targetCache[target] = {
  1213. Root = root,
  1214. Humanoid = humanoid,
  1215. LastName = nameLabel.Text,
  1216. LastDistance = "",
  1217. }
  1218. end
  1219.  
  1220.  
  1221. -----------------------------------------------------------
  1222. -- REFRESH TARGET CACHE
  1223. -----------------------------------------------------------
  1224.  
  1225. local function refreshTargetCache(target)
  1226.  
  1227. local cache =
  1228. targetCache[target]
  1229.  
  1230. if not cache then
  1231. return
  1232. end
  1233.  
  1234. if currentMode == "Players" then
  1235.  
  1236. local character =
  1237. target.Character
  1238.  
  1239. if not character then
  1240. cache.Root = nil
  1241. cache.Humanoid = nil
  1242. return
  1243. end
  1244.  
  1245. cache.Root =
  1246. getCharacterRoot(character)
  1247.  
  1248. cache.Humanoid =
  1249. character:FindFirstChildOfClass(
  1250. "Humanoid"
  1251. )
  1252.  
  1253. else
  1254.  
  1255. cache.Root =
  1256. getNPCRoot(target)
  1257.  
  1258. cache.Humanoid =
  1259. target:FindFirstChildOfClass(
  1260. "Humanoid"
  1261. )
  1262. end
  1263. end
  1264.  
  1265.  
  1266. -----------------------------------------------------------
  1267. -- UPDATE LABEL TEXT
  1268. -----------------------------------------------------------
  1269.  
  1270. local function updateLabelText(
  1271. target,
  1272. data,
  1273. cache,
  1274. distance
  1275. )
  1276.  
  1277. if currentMode == "Players" then
  1278.  
  1279. local name =
  1280. getTargetName(target)
  1281.  
  1282. if cache.LastName ~= name then
  1283.  
  1284. data.NameLabel.Text =
  1285. name
  1286.  
  1287. cache.LastName =
  1288. name
  1289. end
  1290.  
  1291. else
  1292.  
  1293. local name =
  1294. getTargetName(target)
  1295.  
  1296. if cache.LastName ~= name then
  1297.  
  1298. data.NameLabel.Text =
  1299. name
  1300.  
  1301. cache.LastName =
  1302. name
  1303. end
  1304. end
  1305.  
  1306. local distanceText =
  1307. tostring(
  1308. math.floor(distance)
  1309. ) .. "m"
  1310.  
  1311. if cache.LastDistance
  1312. ~= distanceText then
  1313.  
  1314. data.DistanceLabel.Text =
  1315. distanceText
  1316.  
  1317. cache.LastDistance =
  1318. distanceText
  1319. end
  1320. end
  1321.  
  1322.  
  1323. -----------------------------------------------------------
  1324. -- UPDATE TARGET POSITION
  1325. -----------------------------------------------------------
  1326.  
  1327. local function calculateTargetPosition(
  1328. target,
  1329. data,
  1330. playerRoot
  1331. )
  1332.  
  1333. local cache =
  1334. targetCache[target]
  1335.  
  1336. if not cache then
  1337. return nil
  1338. end
  1339.  
  1340. local root =
  1341. cache.Root
  1342.  
  1343. local humanoid =
  1344. cache.Humanoid
  1345.  
  1346. if not root
  1347. or not root.Parent then
  1348.  
  1349. refreshTargetCache(target)
  1350.  
  1351. root =
  1352. cache.Root
  1353.  
  1354. humanoid =
  1355. cache.Humanoid
  1356. end
  1357.  
  1358. if not root
  1359. or not root:IsA("BasePart")
  1360. or not root.Parent then
  1361.  
  1362. data.Dot.Visible = false
  1363. return nil
  1364. end
  1365.  
  1366. if humanoid
  1367. and (
  1368. humanoid.Health <= 0
  1369. or not humanoid.Parent
  1370. ) then
  1371.  
  1372. data.Dot.Visible = false
  1373. return nil
  1374. end
  1375.  
  1376. if currentMode == "NPC"
  1377. and root.Anchored then
  1378.  
  1379. data.Dot.Visible = false
  1380. return nil
  1381. end
  1382.  
  1383. -------------------------------------------------------
  1384. -- Local coordinates.
  1385. --
  1386. -- X = right/left
  1387. -- -Z = forward
  1388. -------------------------------------------------------
  1389.  
  1390. local localPosition =
  1391. playerRoot.CFrame:PointToObjectSpace(
  1392. root.Position
  1393. )
  1394.  
  1395. -------------------------------------------------------
  1396. -- Horizontal distance only.
  1397. -------------------------------------------------------
  1398.  
  1399. local distanceSquared =
  1400. localPosition.X
  1401. * localPosition.X
  1402. +
  1403. localPosition.Z
  1404. * localPosition.Z
  1405.  
  1406. local maxDistanceSquared =
  1407. MAX_DISTANCE
  1408. * MAX_DISTANCE
  1409.  
  1410. if distanceSquared
  1411. > maxDistanceSquared then
  1412.  
  1413. data.Dot.Visible = false
  1414. return nil
  1415. end
  1416.  
  1417. local distance =
  1418. math.sqrt(
  1419. distanceSquared
  1420. )
  1421.  
  1422. -------------------------------------------------------
  1423. -- Radar coordinates.
  1424. -------------------------------------------------------
  1425.  
  1426. local xOffset =
  1427. (
  1428. localPosition.X
  1429. / MAX_DISTANCE
  1430. )
  1431. * (RADAR_WIDTH / 2)
  1432.  
  1433. local yOffset =
  1434. (
  1435. -localPosition.Z
  1436. / MAX_DISTANCE
  1437. )
  1438. * (RADAR_HEIGHT / 2)
  1439.  
  1440. -------------------------------------------------------
  1441. -- Clamp dot.
  1442. -------------------------------------------------------
  1443.  
  1444. local maxX =
  1445. math.max(
  1446. 0,
  1447. (RADAR_WIDTH / 2)
  1448. - (DOT_SIZE / 2)
  1449. )
  1450.  
  1451. local maxY =
  1452. math.max(
  1453. 0,
  1454. (RADAR_HEIGHT / 2)
  1455. - (DOT_SIZE / 2)
  1456. )
  1457.  
  1458. xOffset =
  1459. math.clamp(
  1460. xOffset,
  1461. -maxX,
  1462. maxX
  1463. )
  1464.  
  1465. yOffset =
  1466. math.clamp(
  1467. yOffset,
  1468. -maxY,
  1469. maxY
  1470. )
  1471.  
  1472. data.Dot.Visible = true
  1473.  
  1474. data.Dot.Position =
  1475. UDim2.new(
  1476. 0.5,
  1477. xOffset - DOT_SIZE / 2,
  1478.  
  1479. 0.5,
  1480. yOffset - DOT_SIZE / 2
  1481. )
  1482.  
  1483. updateLabelText(
  1484. target,
  1485. data,
  1486. cache,
  1487. distance
  1488. )
  1489.  
  1490. return {
  1491. Target = target,
  1492. Data = data,
  1493. X = xOffset,
  1494. Y = yOffset,
  1495. Distance = distance,
  1496. }
  1497. end
  1498.  
  1499.  
  1500. -----------------------------------------------------------
  1501. -- LABEL COLLISION RESOLUTION
  1502. -----------------------------------------------------------
  1503.  
  1504. local function resolveLabelCollisions(visibleTargets)
  1505.  
  1506. local count =
  1507. #visibleTargets
  1508.  
  1509. if count <= 1 then
  1510. if count == 1 then
  1511.  
  1512. local item =
  1513. visibleTargets[1]
  1514.  
  1515. item.Data.NameLabel.Visible = true
  1516. item.Data.DistanceLabel.Visible = true
  1517.  
  1518. item.Data.NameLabel.Position =
  1519. UDim2.new(
  1520. 0.5,
  1521. 0,
  1522. 0,
  1523. -3
  1524. )
  1525.  
  1526. item.Data.DistanceLabel.Position =
  1527. UDim2.new(
  1528. 0.5,
  1529. 0,
  1530. 1,
  1531. 3
  1532. )
  1533. end
  1534.  
  1535. return
  1536. end
  1537.  
  1538. for i = 1, count do
  1539.  
  1540. local item =
  1541. visibleTargets[i]
  1542.  
  1543. local closestDistance =
  1544. math.huge
  1545.  
  1546. local closestItem = nil
  1547.  
  1548. for j = 1, count do
  1549.  
  1550. if i ~= j then
  1551.  
  1552. local other =
  1553. visibleTargets[j]
  1554.  
  1555. local dx =
  1556. item.X - other.X
  1557.  
  1558. local dy =
  1559. item.Y - other.Y
  1560.  
  1561. local distance =
  1562. math.sqrt(
  1563. dx * dx
  1564. +
  1565. dy * dy
  1566. )
  1567.  
  1568. if distance < closestDistance then
  1569.  
  1570. closestDistance =
  1571. distance
  1572.  
  1573. closestItem =
  1574. other
  1575. end
  1576. end
  1577. end
  1578.  
  1579. ---------------------------------------------------
  1580. -- Isolated target.
  1581. ---------------------------------------------------
  1582.  
  1583. if not closestItem
  1584. or closestDistance
  1585. > LABEL_COLLISION_DISTANCE then
  1586.  
  1587. item.Data.NameLabel.Visible = true
  1588. item.Data.DistanceLabel.Visible = true
  1589.  
  1590. item.Data.NameLabel.Position =
  1591. UDim2.new(
  1592. 0.5,
  1593. 0,
  1594. 0,
  1595. -3
  1596. )
  1597.  
  1598. item.Data.DistanceLabel.Position =
  1599. UDim2.new(
  1600. 0.5,
  1601. 0,
  1602. 1,
  1603. 3
  1604. )
  1605.  
  1606. else
  1607.  
  1608. ------------------------------------------------
  1609. -- Nearby target.
  1610. --
  1611. -- Alternate the label vertically so two
  1612. -- neighboring targets don't have identical
  1613. -- label positions.
  1614. ------------------------------------------------
  1615.  
  1616. local targetIndex =
  1617. table.find(
  1618. visibleTargets,
  1619. item
  1620. )
  1621.  
  1622. local side
  1623.  
  1624. if targetIndex % 2 == 0 then
  1625. side = 1
  1626. else
  1627. side = -1
  1628. end
  1629.  
  1630. local verticalOffset =
  1631. LABEL_OFFSET * side
  1632.  
  1633. item.Data.NameLabel.Visible = true
  1634. item.Data.DistanceLabel.Visible = true
  1635.  
  1636. item.Data.NameLabel.Position =
  1637. UDim2.new(
  1638. 0.5,
  1639. 0,
  1640. 0,
  1641. -3
  1642. + verticalOffset
  1643. )
  1644.  
  1645. item.Data.DistanceLabel.Position =
  1646. UDim2.new(
  1647. 0.5,
  1648. 0,
  1649. 1,
  1650. 3
  1651. + verticalOffset
  1652. )
  1653.  
  1654. ------------------------------------------------
  1655. -- Extremely crowded areas:
  1656. -- Keep the dots visible but reduce label
  1657. -- clutter.
  1658. ------------------------------------------------
  1659.  
  1660. local crowdedCount = 0
  1661.  
  1662. for j = 1, count do
  1663.  
  1664. if i ~= j then
  1665.  
  1666. local other =
  1667. visibleTargets[j]
  1668.  
  1669. local dx =
  1670. item.X - other.X
  1671.  
  1672. local dy =
  1673. item.Y - other.Y
  1674.  
  1675. local distance =
  1676. math.sqrt(
  1677. dx * dx
  1678. +
  1679. dy * dy
  1680. )
  1681.  
  1682. if distance
  1683. <= LABEL_COLLISION_DISTANCE then
  1684.  
  1685. crowdedCount += 1
  1686. end
  1687. end
  1688. end
  1689.  
  1690. if crowdedCount
  1691. >= MAX_LABELS_PER_CLUSTER then
  1692.  
  1693. -- Show labels for the closest targets
  1694. -- and hide the rest.
  1695. if targetIndex
  1696. > MAX_LABELS_PER_CLUSTER then
  1697.  
  1698. item.Data.NameLabel.Visible = false
  1699. item.Data.DistanceLabel.Visible = false
  1700. end
  1701. end
  1702. end
  1703. end
  1704. end
  1705.  
  1706.  
  1707. -----------------------------------------------------------
  1708. -- UPDATE RADAR
  1709. -----------------------------------------------------------
  1710.  
  1711. local function updateRadar()
  1712.  
  1713. if destroyed then
  1714. return
  1715. end
  1716.  
  1717. local character =
  1718. player.Character
  1719.  
  1720. if not character then
  1721. localRoot = nil
  1722.  
  1723. for _, data in pairs(activeDots) do
  1724. data.Dot.Visible = false
  1725. end
  1726.  
  1727. return
  1728. end
  1729.  
  1730. localRoot =
  1731. getCharacterRoot(character)
  1732.  
  1733. if not localRoot then
  1734.  
  1735. for _, data in pairs(activeDots) do
  1736. data.Dot.Visible = false
  1737. end
  1738.  
  1739. return
  1740. end
  1741.  
  1742. local visibleTargets = {}
  1743.  
  1744. -------------------------------------------------------
  1745. -- Update every tracked target.
  1746. -------------------------------------------------------
  1747.  
  1748. for target, data in pairs(activeDots) do
  1749.  
  1750. if destroyed then
  1751. return
  1752. end
  1753.  
  1754. if not target
  1755. or not target.Parent then
  1756.  
  1757. removeDot(target)
  1758.  
  1759. else
  1760.  
  1761. local position =
  1762. calculateTargetPosition(
  1763. target,
  1764. data,
  1765. localRoot
  1766. )
  1767.  
  1768. if position then
  1769.  
  1770. visibleTargets[
  1771. #visibleTargets + 1
  1772. ] = position
  1773. end
  1774. end
  1775. end
  1776.  
  1777. -------------------------------------------------------
  1778. -- Resolve label collisions only after all positions
  1779. -- are calculated.
  1780. -------------------------------------------------------
  1781.  
  1782. resolveLabelCollisions(
  1783. visibleTargets
  1784. )
  1785. end
  1786.  
  1787.  
  1788. -----------------------------------------------------------
  1789. -- PLAYER REGISTRATION
  1790. -----------------------------------------------------------
  1791.  
  1792. local function unregisterPlayer(targetPlayer)
  1793.  
  1794. removeDot(targetPlayer)
  1795.  
  1796. local connectionData =
  1797. playerConnections[targetPlayer]
  1798.  
  1799. if not connectionData then
  1800. return
  1801. end
  1802.  
  1803. for _, connection in pairs(
  1804. connectionData
  1805. ) do
  1806.  
  1807. if connection then
  1808.  
  1809. pcall(function()
  1810. connection:Disconnect()
  1811. end)
  1812. end
  1813. end
  1814.  
  1815. playerConnections[targetPlayer] = nil
  1816. end
  1817.  
  1818.  
  1819. local function registerPlayer(targetPlayer)
  1820.  
  1821. if destroyed
  1822. or not targetPlayer
  1823. or targetPlayer == player then
  1824.  
  1825. return
  1826. end
  1827.  
  1828. if playerConnections[targetPlayer] then
  1829. return
  1830. end
  1831.  
  1832. local characterConnection =
  1833. targetPlayer.CharacterAdded:Connect(
  1834. function(character)
  1835.  
  1836. if destroyed then
  1837. return
  1838. end
  1839.  
  1840. ------------------------------------------------
  1841. -- Update cache immediately.
  1842. ------------------------------------------------
  1843.  
  1844. local cache =
  1845. targetCache[targetPlayer]
  1846.  
  1847. if cache then
  1848.  
  1849. cache.Root =
  1850. getCharacterRoot(
  1851. character
  1852. )
  1853.  
  1854. cache.Humanoid =
  1855. character:FindFirstChildOfClass(
  1856. "Humanoid"
  1857. )
  1858. end
  1859.  
  1860. if currentMode == "Players" then
  1861.  
  1862. task.defer(function()
  1863.  
  1864. if destroyed
  1865. or not targetPlayer.Parent then
  1866.  
  1867. return
  1868. end
  1869.  
  1870. if isTrackablePlayer(
  1871. targetPlayer
  1872. ) then
  1873.  
  1874. createDot(
  1875. targetPlayer
  1876. )
  1877. end
  1878. end)
  1879. end
  1880. end
  1881. )
  1882.  
  1883. playerConnections[targetPlayer] = {
  1884. CharacterAdded =
  1885. characterConnection
  1886. }
  1887.  
  1888. if currentMode == "Players"
  1889. and isTrackablePlayer(targetPlayer) then
  1890.  
  1891. createDot(targetPlayer)
  1892. end
  1893. end
  1894.  
  1895.  
  1896. -----------------------------------------------------------
  1897. -- PLAYER ADDED
  1898. -----------------------------------------------------------
  1899.  
  1900. connections.PlayerAdded =
  1901. Players.PlayerAdded:Connect(
  1902. function(targetPlayer)
  1903.  
  1904. if destroyed then
  1905. return
  1906. end
  1907.  
  1908. safeCall(
  1909. "Player Added",
  1910. registerPlayer,
  1911. targetPlayer
  1912. )
  1913. end
  1914. )
  1915.  
  1916.  
  1917. -----------------------------------------------------------
  1918. -- PLAYER REMOVING
  1919. -----------------------------------------------------------
  1920.  
  1921. connections.PlayerRemoving =
  1922. Players.PlayerRemoving:Connect(
  1923. function(targetPlayer)
  1924.  
  1925. if destroyed then
  1926. return
  1927. end
  1928.  
  1929. safeCall(
  1930. "Player Removing",
  1931. unregisterPlayer,
  1932. targetPlayer
  1933. )
  1934. end
  1935. )
  1936.  
  1937.  
  1938. -----------------------------------------------------------
  1939. -- REGISTER EXISTING PLAYERS
  1940. -----------------------------------------------------------
  1941.  
  1942. for _, targetPlayer in ipairs(
  1943. Players:GetPlayers()
  1944. ) do
  1945.  
  1946. if targetPlayer ~= player then
  1947. registerPlayer(targetPlayer)
  1948. end
  1949. end
  1950.  
  1951.  
  1952. -----------------------------------------------------------
  1953. -- LOCAL PLAYER RESPAWN
  1954. -----------------------------------------------------------
  1955.  
  1956. connections.CharacterAdded =
  1957. player.CharacterAdded:Connect(
  1958. function(character)
  1959.  
  1960. if destroyed then
  1961. return
  1962. end
  1963.  
  1964. localRoot = nil
  1965.  
  1966. task.defer(function()
  1967.  
  1968. if destroyed
  1969. or not character.Parent then
  1970.  
  1971. return
  1972. end
  1973.  
  1974. localRoot =
  1975. getCharacterRoot(
  1976. character
  1977. )
  1978.  
  1979. updateRadar()
  1980. end)
  1981. end
  1982. )
  1983.  
  1984.  
  1985. -----------------------------------------------------------
  1986. -- NPC MODEL DETECTION
  1987. -----------------------------------------------------------
  1988.  
  1989. local function findPotentialNPC(obj)
  1990.  
  1991. if not obj then
  1992. return nil
  1993. end
  1994.  
  1995. if obj:IsA("Model") then
  1996. return obj
  1997. end
  1998.  
  1999. if obj:IsA("Humanoid")
  2000. or obj:IsA("BasePart") then
  2001.  
  2002. return obj:FindFirstAncestorOfClass(
  2003. "Model"
  2004. )
  2005. end
  2006.  
  2007. return obj:FindFirstAncestorOfClass(
  2008. "Model"
  2009. )
  2010. end
  2011.  
  2012.  
  2013. -----------------------------------------------------------
  2014. -- FAST NPC DETECTION
  2015. -----------------------------------------------------------
  2016.  
  2017. connections.DescendantAdded =
  2018. workspace.DescendantAdded:Connect(
  2019. function(obj)
  2020.  
  2021. if destroyed
  2022. or currentMode ~= "NPC" then
  2023.  
  2024. return
  2025. end
  2026.  
  2027. local npc =
  2028. findPotentialNPC(obj)
  2029.  
  2030. if not npc then
  2031. return
  2032. end
  2033.  
  2034. ------------------------------------------------
  2035. -- Defer only to allow the model's inserted
  2036. -- children to finish replication.
  2037. ------------------------------------------------
  2038.  
  2039. task.defer(function()
  2040.  
  2041. if destroyed
  2042. or currentMode ~= "NPC"
  2043. or not npc.Parent then
  2044.  
  2045. return
  2046. end
  2047.  
  2048. if isTrackableNPC(npc) then
  2049.  
  2050. createDot(npc)
  2051. end
  2052. end)
  2053. end
  2054. )
  2055.  
  2056.  
  2057. -----------------------------------------------------------
  2058. -- REFRESH PLAYERS
  2059. -----------------------------------------------------------
  2060.  
  2061. local function refreshPlayers()
  2062.  
  2063. if destroyed
  2064. or currentMode ~= "Players" then
  2065.  
  2066. return
  2067. end
  2068.  
  2069. for _, targetPlayer in ipairs(
  2070. Players:GetPlayers()
  2071. ) do
  2072.  
  2073. if targetPlayer ~= player then
  2074.  
  2075. if isTrackablePlayer(
  2076. targetPlayer
  2077. ) then
  2078.  
  2079. createDot(
  2080. targetPlayer
  2081. )
  2082. end
  2083. end
  2084. end
  2085. end
  2086.  
  2087.  
  2088. -----------------------------------------------------------
  2089. -- REFRESH NPCS
  2090. -----------------------------------------------------------
  2091.  
  2092. local function refreshNPCs()
  2093.  
  2094. if destroyed
  2095. or currentMode ~= "NPC" then
  2096.  
  2097. return
  2098. end
  2099.  
  2100. -------------------------------------------------------
  2101. -- This only runs during safety synchronization or
  2102. -- initial NPC mode activation.
  2103. -------------------------------------------------------
  2104.  
  2105. for _, obj in ipairs(
  2106. workspace:GetDescendants()
  2107. ) do
  2108.  
  2109. if destroyed
  2110. or currentMode ~= "NPC" then
  2111.  
  2112. break
  2113. end
  2114.  
  2115. if obj:IsA("Model")
  2116. and isTrackableNPC(obj) then
  2117.  
  2118. createDot(obj)
  2119. end
  2120. end
  2121. end
  2122.  
  2123.  
  2124. -----------------------------------------------------------
  2125. -- MODE SWITCH
  2126. -----------------------------------------------------------
  2127.  
  2128. local function switchMode()
  2129.  
  2130. if destroyed then
  2131. return
  2132. end
  2133.  
  2134. if currentMode == "Players" then
  2135. currentMode = "NPC"
  2136. else
  2137. currentMode = "Players"
  2138. end
  2139.  
  2140. -------------------------------------------------------
  2141. -- Clear previous mode.
  2142. -------------------------------------------------------
  2143.  
  2144. removeAllDots()
  2145.  
  2146. -------------------------------------------------------
  2147. -- Update button.
  2148. -------------------------------------------------------
  2149.  
  2150. modeButton.Text =
  2151. "MODE: "
  2152. .. string.upper(
  2153. currentMode
  2154. )
  2155.  
  2156. -------------------------------------------------------
  2157. -- Load new mode.
  2158. -------------------------------------------------------
  2159.  
  2160. if currentMode == "Players" then
  2161. refreshPlayers()
  2162. else
  2163. refreshNPCs()
  2164. end
  2165.  
  2166. updateRadar()
  2167. end
  2168.  
  2169.  
  2170. -----------------------------------------------------------
  2171. -- MODE BUTTON
  2172. -----------------------------------------------------------
  2173.  
  2174. connections.ModeButton =
  2175. modeButton.MouseButton1Click:Connect(
  2176. function()
  2177.  
  2178. if destroyed then
  2179. return
  2180. end
  2181.  
  2182. safeCall(
  2183. "Mode Switch",
  2184. switchMode
  2185. )
  2186. end
  2187. )
  2188.  
  2189.  
  2190. -----------------------------------------------------------
  2191. -- RANGE INPUT
  2192. -----------------------------------------------------------
  2193.  
  2194. connections.RangeFocusLost =
  2195. rangeBox.FocusLost:Connect(
  2196. function()
  2197.  
  2198. if destroyed then
  2199. return
  2200. end
  2201.  
  2202. safeCall(
  2203. "Range Input",
  2204. function()
  2205.  
  2206. local newRange =
  2207. tonumber(
  2208. rangeBox.Text
  2209. )
  2210.  
  2211. if newRange
  2212. and newRange == newRange
  2213. and newRange ~= math.huge
  2214. and newRange ~= -math.huge
  2215. and newRange > 0 then
  2216.  
  2217. MAX_DISTANCE =
  2218. math.clamp(
  2219. math.floor(
  2220. newRange
  2221. ),
  2222. MIN_RANGE,
  2223. MAX_RANGE
  2224. )
  2225. end
  2226.  
  2227. rangeBox.Text =
  2228. tostring(
  2229. MAX_DISTANCE
  2230. )
  2231.  
  2232. updateRadar()
  2233. end
  2234. )
  2235. end
  2236. )
  2237.  
  2238.  
  2239. -----------------------------------------------------------
  2240. -- RADAR SIZE
  2241. -----------------------------------------------------------
  2242.  
  2243. local originalAspectRatio =
  2244. RADAR_HEIGHT / RADAR_WIDTH
  2245.  
  2246.  
  2247. local function updateRadarSize(newSize)
  2248.  
  2249. if destroyed then
  2250. return
  2251. end
  2252.  
  2253. if typeof(newSize) ~= "number"
  2254. or newSize ~= newSize
  2255. or newSize == math.huge
  2256. or newSize == -math.huge then
  2257.  
  2258. sizeBox.Text =
  2259. tostring(
  2260. RADAR_WIDTH
  2261. )
  2262.  
  2263. return
  2264. end
  2265.  
  2266. newSize =
  2267. math.clamp(
  2268. math.floor(
  2269. newSize
  2270. ),
  2271. MIN_RADAR_SIZE,
  2272. MAX_RADAR_SIZE
  2273. )
  2274.  
  2275. RADAR_WIDTH =
  2276. newSize
  2277.  
  2278. RADAR_HEIGHT =
  2279. math.max(
  2280. 1,
  2281. math.floor(
  2282. RADAR_WIDTH
  2283. * originalAspectRatio
  2284. )
  2285. )
  2286.  
  2287. -------------------------------------------------------
  2288. -- Container
  2289. -------------------------------------------------------
  2290.  
  2291. mainContainer.Size =
  2292. UDim2.new(
  2293. 0,
  2294. RADAR_WIDTH,
  2295. 0,
  2296. RADAR_HEIGHT + 105
  2297. )
  2298.  
  2299. -------------------------------------------------------
  2300. -- Button
  2301. -------------------------------------------------------
  2302.  
  2303. modeButton.Size =
  2304. UDim2.new(
  2305. 0,
  2306. RADAR_WIDTH,
  2307. 0,
  2308. 28
  2309. )
  2310.  
  2311. -------------------------------------------------------
  2312. -- Radar
  2313. -------------------------------------------------------
  2314.  
  2315. radarFrame.Size =
  2316. UDim2.new(
  2317. 0,
  2318. RADAR_WIDTH,
  2319. 0,
  2320. RADAR_HEIGHT
  2321. )
  2322.  
  2323. -------------------------------------------------------
  2324. -- Maintain horizontal center.
  2325. -------------------------------------------------------
  2326.  
  2327. mainContainer.Position =
  2328. UDim2.new(
  2329. 0.5,
  2330. -RADAR_WIDTH / 2,
  2331. mainContainer.Position.Y.Scale,
  2332. mainContainer.Position.Y.Offset
  2333. )
  2334.  
  2335. -------------------------------------------------------
  2336. -- Bottom controls.
  2337. -------------------------------------------------------
  2338.  
  2339. sizeLabel.Position =
  2340. UDim2.new(
  2341. 0,
  2342. 0,
  2343. 0,
  2344. RADAR_HEIGHT + 70
  2345. )
  2346.  
  2347. sizeBox.Position =
  2348. UDim2.new(
  2349. 0.5,
  2350. -5,
  2351. 0,
  2352. RADAR_HEIGHT + 70
  2353. )
  2354.  
  2355. sizeBox.Text =
  2356. tostring(
  2357. RADAR_WIDTH
  2358. )
  2359.  
  2360. updateRadar()
  2361. end
  2362.  
  2363.  
  2364. connections.SizeFocusLost =
  2365. sizeBox.FocusLost:Connect(
  2366. function()
  2367.  
  2368. if destroyed then
  2369. return
  2370. end
  2371.  
  2372. safeCall(
  2373. "Size Input",
  2374. function()
  2375.  
  2376. local newSize =
  2377. tonumber(
  2378. sizeBox.Text
  2379. )
  2380.  
  2381. if newSize then
  2382.  
  2383. updateRadarSize(
  2384. newSize
  2385. )
  2386.  
  2387. else
  2388.  
  2389. sizeBox.Text =
  2390. tostring(
  2391. RADAR_WIDTH
  2392. )
  2393. end
  2394. end
  2395. )
  2396. end
  2397. )
  2398.  
  2399.  
  2400. -----------------------------------------------------------
  2401. -- DRAGGING
  2402. -----------------------------------------------------------
  2403.  
  2404. connections.MainInputBegan =
  2405. mainContainer.InputBegan:Connect(
  2406. function(input)
  2407.  
  2408. if destroyed then
  2409. return
  2410. end
  2411.  
  2412. if input.UserInputType
  2413. ~= Enum.UserInputType.MouseButton1
  2414. and input.UserInputType
  2415. ~= Enum.UserInputType.Touch then
  2416.  
  2417. return
  2418. end
  2419.  
  2420. if UserInputService:GetFocusedTextBox() then
  2421. return
  2422. end
  2423.  
  2424. dragging = true
  2425.  
  2426. dragStart =
  2427. input.Position
  2428.  
  2429. startPos =
  2430. mainContainer.Position
  2431. end
  2432. )
  2433.  
  2434.  
  2435. connections.InputEnded =
  2436. UserInputService.InputEnded:Connect(
  2437. function(input)
  2438.  
  2439. if input.UserInputType
  2440. == Enum.UserInputType.MouseButton1
  2441. or input.UserInputType
  2442. == Enum.UserInputType.Touch then
  2443.  
  2444. dragging = false
  2445. end
  2446. end
  2447. )
  2448.  
  2449.  
  2450. connections.InputChanged =
  2451. UserInputService.InputChanged:Connect(
  2452. function(input)
  2453.  
  2454. if destroyed
  2455. or not dragging then
  2456.  
  2457. return
  2458. end
  2459.  
  2460. if input.UserInputType
  2461. ~= Enum.UserInputType.MouseMovement
  2462. and input.UserInputType
  2463. ~= Enum.UserInputType.Touch then
  2464.  
  2465. return
  2466. end
  2467.  
  2468. if not dragStart
  2469. or not startPos then
  2470.  
  2471. dragging = false
  2472. return
  2473. end
  2474.  
  2475. local delta =
  2476. input.Position
  2477. - dragStart
  2478.  
  2479. mainContainer.Position =
  2480. UDim2.new(
  2481. startPos.X.Scale,
  2482. startPos.X.Offset
  2483. + delta.X,
  2484.  
  2485. startPos.Y.Scale,
  2486. startPos.Y.Offset
  2487. + delta.Y
  2488. )
  2489. end
  2490. )
  2491.  
  2492.  
  2493. -----------------------------------------------------------
  2494. -- SAFETY SYNCHRONIZATION
  2495. -----------------------------------------------------------
  2496.  
  2497. task.spawn(function()
  2498.  
  2499. while not destroyed
  2500. and screenGui.Parent do
  2501.  
  2502. task.wait(
  2503. SAFETY_SCAN_RATE
  2504. )
  2505.  
  2506. if destroyed
  2507. or not screenGui.Parent then
  2508.  
  2509. break
  2510. end
  2511.  
  2512. safeCall(
  2513. "Safety Synchronization",
  2514. function()
  2515.  
  2516. if currentMode == "Players" then
  2517.  
  2518. ------------------------------------------------
  2519. -- Repair missing player registrations.
  2520. ------------------------------------------------
  2521.  
  2522. local validPlayers = {}
  2523.  
  2524. for _, targetPlayer in ipairs(
  2525. Players:GetPlayers()
  2526. ) do
  2527.  
  2528. if targetPlayer ~= player then
  2529.  
  2530. validPlayers[
  2531. targetPlayer
  2532. ] = true
  2533.  
  2534. if not playerConnections[
  2535. targetPlayer
  2536. ] then
  2537.  
  2538. registerPlayer(
  2539. targetPlayer
  2540. )
  2541. end
  2542.  
  2543. if isTrackablePlayer(
  2544. targetPlayer
  2545. )
  2546. and not activeDots[
  2547. targetPlayer
  2548. ] then
  2549.  
  2550. createDot(
  2551. targetPlayer
  2552. )
  2553. end
  2554. end
  2555. end
  2556.  
  2557. ------------------------------------------------
  2558. -- Remove stale players.
  2559. ------------------------------------------------
  2560.  
  2561. local stale = {}
  2562.  
  2563. for target in pairs(
  2564. activeDots
  2565. ) do
  2566.  
  2567. if not validPlayers[
  2568. target
  2569. ] then
  2570.  
  2571. stale[
  2572. #stale + 1
  2573. ] = target
  2574. end
  2575. end
  2576.  
  2577. for _, target in ipairs(stale) do
  2578. removeDot(target)
  2579. end
  2580.  
  2581. else
  2582.  
  2583. ------------------------------------------------
  2584. -- NPC safety repair.
  2585. --
  2586. -- This is the ONLY place the full workspace
  2587. -- scan occurs.
  2588. ------------------------------------------------
  2589.  
  2590. refreshNPCs()
  2591.  
  2592. ------------------------------------------------
  2593. -- Remove dead/deleted NPCs.
  2594. ------------------------------------------------
  2595.  
  2596. local staleNPCs = {}
  2597.  
  2598. for model in pairs(
  2599. activeDots
  2600. ) do
  2601.  
  2602. if not model
  2603. or not model.Parent
  2604. or not isTrackableNPC(
  2605. model
  2606. ) then
  2607.  
  2608. staleNPCs[
  2609. #staleNPCs + 1
  2610. ] = model
  2611. end
  2612. end
  2613.  
  2614. for _, model in ipairs(
  2615. staleNPCs
  2616. ) do
  2617.  
  2618. removeDot(model)
  2619. end
  2620. end
  2621.  
  2622. updateRadar()
  2623. end
  2624. )
  2625. end
  2626. end)
  2627.  
  2628.  
  2629. -----------------------------------------------------------
  2630. -- OPTIMIZED RADAR LOOP
  2631. -----------------------------------------------------------
  2632.  
  2633. connections.RenderStepped =
  2634. RunService.RenderStepped:Connect(
  2635. function(deltaTime)
  2636.  
  2637. if destroyed then
  2638. return
  2639. end
  2640.  
  2641. radarAccumulator += deltaTime
  2642.  
  2643. if radarAccumulator
  2644. < RADAR_UPDATE_RATE then
  2645.  
  2646. return
  2647. end
  2648.  
  2649. ---------------------------------------------------
  2650. -- Prevent accumulator from growing indefinitely
  2651. -- after frame drops.
  2652. ---------------------------------------------------
  2653.  
  2654. radarAccumulator = 0
  2655.  
  2656. updateRadar()
  2657. end
  2658. )
  2659.  
  2660.  
  2661. -----------------------------------------------------------
  2662. -- GUI CLEANUP
  2663. -----------------------------------------------------------
  2664.  
  2665. connections.GuiDestroying =
  2666. screenGui.Destroying:Connect(
  2667. function()
  2668.  
  2669. if destroyed then
  2670. return
  2671. end
  2672.  
  2673. destroyed = true
  2674.  
  2675. dragging = false
  2676.  
  2677. ---------------------------------------------------
  2678. -- Global connections.
  2679. ---------------------------------------------------
  2680.  
  2681. for name, connection in pairs(
  2682. connections
  2683. ) do
  2684.  
  2685. if connection then
  2686.  
  2687. pcall(function()
  2688. connection:Disconnect()
  2689. end)
  2690.  
  2691. connections[name] = nil
  2692. end
  2693. end
  2694.  
  2695. ---------------------------------------------------
  2696. -- Player connections.
  2697. ---------------------------------------------------
  2698.  
  2699. for targetPlayer, connectionData in pairs(
  2700. playerConnections
  2701. ) do
  2702.  
  2703. for _, connection in pairs(
  2704. connectionData
  2705. ) do
  2706.  
  2707. if connection then
  2708.  
  2709. pcall(function()
  2710. connection:Disconnect()
  2711. end)
  2712. end
  2713. end
  2714.  
  2715. playerConnections[
  2716. targetPlayer
  2717. ] = nil
  2718. end
  2719.  
  2720. ---------------------------------------------------
  2721. -- Remove dots.
  2722. ---------------------------------------------------
  2723.  
  2724. local targets = {}
  2725.  
  2726. for target in pairs(activeDots) do
  2727. targets[#targets + 1] = target
  2728. end
  2729.  
  2730. for _, target in ipairs(targets) do
  2731.  
  2732. local data =
  2733. activeDots[target]
  2734.  
  2735. activeDots[target] = nil
  2736. targetCache[target] = nil
  2737.  
  2738. if data
  2739. and data.Dot then
  2740.  
  2741. pcall(function()
  2742. data.Dot:Destroy()
  2743. end)
  2744. end
  2745. end
  2746. end
  2747. )
  2748.  
  2749.  
  2750. -----------------------------------------------------------
  2751. -- INITIAL LOAD
  2752. -----------------------------------------------------------
  2753.  
  2754. safeCall(
  2755. "Initial Player Load",
  2756. refreshPlayers
  2757. )
  2758.  
  2759. safeCall(
  2760. "Initial Radar Update",
  2761. updateRadar
  2762. )
  2763.  
  2764.  
  2765. -----------------------------------------------------------
  2766. -- FINAL STATUS
  2767. -----------------------------------------------------------
  2768.  
  2769. print(
  2770. "[Delta Radar V26] "
  2771. .. "Optimized radar loaded | "
  2772. .. "Mode: "
  2773. .. currentMode
  2774. .. " | Update: 30Hz"
  2775. )
Advertisement
Add Comment
Please, Sign In to add comment