local RunService = game:GetService("RunService") local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local VirtualInputManager = game:GetService("VirtualInputManager") local Player = Players.LocalPlayer if not Player then error("LocalPlayer tidak ditemukan!") end -- Cache fungsi matematika untuk performa local min = math.min local max = math.max local abs = math.abs local deg = math.deg local acos = math.acos local sqrt = math.sqrt local clamp = math.clamp -- Flag debug (set false jika tidak ingin banyak log) local DEBUG = true local function AdvancedLog(level, message, data) if not DEBUG then return end local timestamp = os.date("%Y-%m-%d %H:%M:%S") print(string.format("[%s] [%s]: %s", timestamp, level, message)) if data then for k, v in pairs(data) do print(string.format(" %s: %s", k, tostring(v))) end end end -- Konstanta State local STATE_IDLE = "Idle" local STATE_TRACKING = "Tracking" local STATE_ANTICIPATING = "Anticipating" local STATE_PARIED = "Parried" local STATE_COOLDOWN = "Cooldown" local STATE_SPAM = "Spam" local MAX_IMPACT_TIME = 4.0 -- Maksimum waktu impact (detik) local INCOMING_ANGLE_THRESHOLD = 20 -- Threshold sudut (derajat) --------------------------------- -- Advanced Parry Controller (State Machine) --------------------------------- local AdvancedParryController = {} AdvancedParryController.__index = AdvancedParryController function AdvancedParryController.new() local self = setmetatable({}, AdvancedParryController) self.Player = Player self.State = STATE_IDLE self.LastParryTime = tick() self.CooldownTime = 0.75 -- Cooldown normal (detik) self.BaseThreshold = 0.45 -- Threshold dasar (detik) self.BallStopped = false self.PreviousVelocity = nil self.SmoothedAccel = Vector3.new(0, 0, 0) self.CurrentBall = nil self.Connection = nil self.LastSpamTime = 0 self.BaseSpamInterval = 0.1 -- GUI Full Screen Popup self.FullScreenGui = nil self.AdditionalInfoLabel = nil -- Overpower Mode: parry akan menjadi jauh lebih agresif dan responsif self.OverpowerMode = true self.OverpowerThresholdMultiplier = 0.5 -- Mengurangi threshold agar trigger lebih cepat self.OverpowerCooldown = 0.1 -- Cooldown sangat rendah pada mode ini return self end -- Dapatkan ping lokal (detik) function AdvancedParryController:GetLocalPing() local success, ping = pcall(function() return self.Player:GetNetworkPing() end) if not success then AdvancedLog("WARN", "Gagal mendapatkan ping, default 0.1 detik.", nil) return 0.1 end return ping or 0.1 end -- Hitung threshold dinamis berdasarkan ping (dikurangi jika OverpowerMode aktif) function AdvancedParryController:ComputeDynamicThreshold() local adjustmentFactor = 0.5 local threshold = self.BaseThreshold + (self:GetLocalPing() * adjustmentFactor) if self.OverpowerMode then threshold = threshold * self.OverpowerThresholdMultiplier end return threshold end -- Seleksi bola target terbaik (paling mengancam) dari folder "Balls" function AdvancedParryController:SelectTargetBall() local ballsFolder = workspace:FindFirstChild("Balls") local bestBall = nil local bestTImpact = math.huge if ballsFolder then local character = self.Player.Character local HRP = character and character:FindFirstChild("HumanoidRootPart") if not HRP then return nil, math.huge end for _, ball in ipairs(ballsFolder:GetChildren()) do if ball:GetAttribute("realBall") and ball:GetAttribute("target") == self.Player.Name then local distance = (HRP.Position - ball.Position).Magnitude local ballVelocity local success = pcall(function() ballVelocity = ball.zoomies.VectorVelocity end) if success and typeof(ballVelocity) == "Vector3" then -- Untuk seleksi, asumsikan percepatan nol local tImpact = self:PredictImpactTime(distance, ballVelocity, Vector3.new(0,0,0)) if tImpact < bestTImpact then bestTImpact = tImpact bestBall = ball end end end end end return bestBall, bestTImpact end -- Reset koneksi event bila diperlukan function AdvancedParryController:ResetConnection() if self.Connection then self.Connection:Disconnect() self.Connection = nil end end -- Inisialisasi listener bola untuk mendeteksi perubahan atribut target function AdvancedParryController:InitializeBallListener() self:ResetConnection() local ball = self:SelectTargetBall() -- Menggunakan target terbaik if ball then self.CurrentBall = ball self.Connection = ball:GetAttributeChangedSignal("target"):Connect(function() self.State = STATE_TRACKING AdvancedLog("INFO", "Atribut target bola berubah, reset state ke TRACKING.", {Ball = ball.Name}) end) AdvancedLog("INFO", "Listener bola diinisialisasi.", {Ball = ball.Name}) else self.CurrentBall = nil AdvancedLog("WARN", "Bola tidak ditemukan. Listener tidak diinisialisasi.", nil) end end -- Inisialisasi GUI full-screen (popup hitam) dengan animasi smooth. -- Tampilan: layar penuh yang sedikit transparan, di tengah ada tulisan "Status : Auto Parry", -- label tambahan untuk informasi penting (misal: Ping & State) dan tombol Exit. function AdvancedParryController:InitializeGUI() local playerGui = self.Player:WaitForChild("PlayerGui") local screenGui = Instance.new("ScreenGui") screenGui.Name = "FullScreenPopupGui" screenGui.ResetOnSpawn = false screenGui.IgnoreGuiInset = true -- Menutupi seluruh layar (termasuk area inset) screenGui.Parent = playerGui -- Frame full screen dengan background hitam (mulai dari transparansi penuh untuk animasi) local fullScreenFrame = Instance.new("Frame") fullScreenFrame.Name = "FullScreenFrame" fullScreenFrame.BackgroundColor3 = Color3.new(0, 0, 0) fullScreenFrame.BackgroundTransparency = 1 -- Mulai transparan, akan tween ke 0.2 fullScreenFrame.Size = UDim2.new(1, 0, 1, 0) fullScreenFrame.Position = UDim2.new(0, 0, 0, 0) fullScreenFrame.Parent = screenGui -- Label utama untuk status (di tengah layar) local statusLabel = Instance.new("TextLabel") statusLabel.Name = "StatusLabel" statusLabel.BackgroundTransparency = 1 statusLabel.Text = "Just - Hub : Auto Parry" statusLabel.TextColor3 = Color3.new(1, 1, 1) statusLabel.Font = Enum.Font.SourceSansBold statusLabel.TextSize = 36 statusLabel.AnchorPoint = Vector2.new(0.5, 0.5) statusLabel.Position = UDim2.new(0.5, 0, 0.35, 0) -- Mulai dengan scale 0 (ukuran 0) untuk animasi scale-in statusLabel.Size = UDim2.new(0, 0, 0, 0) statusLabel.Parent = fullScreenFrame -- Label tambahan untuk informasi penting (misal: ping dan state) local additionalInfoLabel = Instance.new("TextLabel") additionalInfoLabel.Name = "AdditionalInfoLabel" additionalInfoLabel.BackgroundTransparency = 1 additionalInfoLabel.Text = "Loading info..." additionalInfoLabel.TextColor3 = Color3.new(1, 1, 1) additionalInfoLabel.Font = Enum.Font.SourceSans additionalInfoLabel.TextSize = 24 additionalInfoLabel.AnchorPoint = Vector2.new(0.5, 0.5) additionalInfoLabel.Position = UDim2.new(0.5, 0, 0.5, 0) additionalInfoLabel.Size = UDim2.new(0, 400, 0, 50) additionalInfoLabel.Parent = fullScreenFrame self.AdditionalInfoLabel = additionalInfoLabel -- Tombol Exit di bawah label tambahan local exitButton = Instance.new("TextButton") exitButton.Name = "ExitButton" exitButton.Text = "Exit" exitButton.TextColor3 = Color3.new(1, 1, 1) exitButton.Font = Enum.Font.SourceSansBold exitButton.TextSize = 28 exitButton.BackgroundColor3 = Color3.new(0.3, 0.3, 0.3) exitButton.Size = UDim2.new(0, 150, 0, 50) exitButton.AnchorPoint = Vector2.new(0.5, 0) exitButton.Position = UDim2.new(0.5, 0, 0.65, 0) exitButton.Parent = fullScreenFrame exitButton.MouseButton1Click:Connect(function() Player:Kick("Anda telah keluar dari game.") end) self.FullScreenGui = screenGui -- Tween untuk animasi fade-in (background transparency dari 1 ke 0.2) local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out) local tweenFrame = TweenService:Create(fullScreenFrame, tweenInfo, {BackgroundTransparency = 0.2}) tweenFrame:Play() -- Tween untuk animasi scale-in label utama (dari 0 ke ukuran asli) local tweenLabel = TweenService:Create(statusLabel, tweenInfo, {Size = UDim2.new(0, 400, 0, 100)}) tweenLabel:Play() -- Tween untuk animasi scale-in label informasi tambahan (sedikit scale-up) local tweenInfoLabel = TweenService:Create(additionalInfoLabel, tweenInfo, {TextTransparency = 0}) tweenInfoLabel:Play() end -- Fungsi untuk mengirim event parry (klik mouse) dengan delay minimal function AdvancedParryController:AttemptParry() VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0) task.delay(0.01, function() VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0) end) self.LastParryTime = tick() end -- Lakukan exponential smoothing pada percepatan (lebih agresif pada mode overpowered) function AdvancedParryController:SmoothAcceleration(currentVelocity, dt) local alpha = self.OverpowerMode and 0.8 or 0.4 if not self.PreviousVelocity then self.PreviousVelocity = currentVelocity return Vector3.new(0, 0, 0) end local instantAccel = (currentVelocity - self.PreviousVelocity) / dt self.SmoothedAccel = self.SmoothedAccel:Lerp(instantAccel, alpha) self.PreviousVelocity = currentVelocity return self.SmoothedAccel end -- Prediksi waktu impact menggunakan rumus: s = vt + ½at² function AdvancedParryController:PredictImpactTime(distance, currentVelocity, accel) local v = currentVelocity.Magnitude local a = accel.Magnitude if abs(a) < 0.001 then return (v > 0) and (distance / v) or math.huge end local discriminant = v * v + 2 * a * distance if discriminant < 0 then return math.huge end local t1 = (-v + sqrt(discriminant)) / a local t2 = (-v - sqrt(discriminant)) / a local t = math.huge if t1 > 0 then t = t1 end if t2 > 0 and t2 < t then t = t2 end return t end -- Prediksi posisi bola saat impact (asumsi percepatan konstan) function AdvancedParryController:PredictImpactPosition(ballPos, currentVelocity, accel, t) return ballPos + currentVelocity * t + 0.5 * accel * t * t end -- Hitung sudut incoming: antara arah kecepatan bola dan vektor dari bola ke HRP function AdvancedParryController:ComputeIncomingAngle(ballVelocity, HRP, ballPos) local mag = ballVelocity.Magnitude if mag <= 0 then return 180 end local incomingVec = (HRP.Position - ballPos).Unit local velDir = ballVelocity.Unit local dot = clamp(velDir:Dot(incomingVec), -1, 1) return deg(acos(dot)) end -- Update state berdasarkan data bola, karakter, prediksi, dan validasi ekstra function AdvancedParryController:UpdateState(dt) local now = tick() local ball, tImpactCandidate = self:SelectTargetBall() if not ball then self.State = STATE_IDLE AdvancedLog("DEBUG", "Tidak ada bola target ditemukan.", nil) self:InitializeBallListener() return end self.CurrentBall = ball local character = self.Player.Character local HRP = character and character:FindFirstChild("HumanoidRootPart") if not HRP then self.State = STATE_IDLE AdvancedLog("WARN", "HumanoidRootPart tidak ditemukan.", nil) return end local success, ballVelocity = pcall(function() return ball.zoomies.VectorVelocity end) if not success or typeof(ballVelocity) ~= "Vector3" then AdvancedLog("ERROR", "VectorVelocity bola tidak valid.", {Error = tostring(ballVelocity)}) return end local speed = ballVelocity.Magnitude local dynamicThreshold = self:ComputeDynamicThreshold() local threshold = min(dynamicThreshold, MAX_IMPACT_TIME) local distance = (HRP.Position - ball.Position).Magnitude -- Hitung percepatan, waktu impact, dan incoming angle local prevVelocity = self.PreviousVelocity or ballVelocity local accel = self:SmoothAcceleration(ballVelocity, dt) local tImpact = self:PredictImpactTime(distance, ballVelocity, accel) local incomingAngle = self:ComputeIncomingAngle(ballVelocity, HRP, ball.Position) -- Validasi target bola local targetAttr = ball:GetAttribute("target") if type(targetAttr) ~= "string" or targetAttr ~= self.Player.Name then self.State = STATE_IDLE AdvancedLog("DEBUG", "Bola tidak mengincar local player.", {Target = tostring(targetAttr)}) return end -- MODE OVERPOWER: Jika tImpact sudah sangat rendah, trigger parry langsung (bypass kondisi normal) if self.OverpowerMode and tImpact <= threshold * 1.5 then self:AttemptParry() self.State = STATE_PARIED AdvancedLog("ACTION", "OVERPOWER: Forced parry triggered.", {tImpact = tImpact, IncomingAngle = incomingAngle}) end -- Jika bola berada dalam rentang 10-20 studs dengan incoming angle rendah if distance >= 10 and distance <= 20 and incomingAngle < INCOMING_ANGLE_THRESHOLD then self:AttemptParry() self.State = STATE_PARIED AdvancedLog("ACTION", "Direct parry triggered.", {Distance = distance, Angle = incomingAngle, tImpact = tImpact}) end -- Mode SPAM: Jika bola sangat dekat (<= 10 studs) if distance <= 10 then if self.State ~= STATE_SPAM then self.State = STATE_SPAM AdvancedLog("INFO", "Entering SPAM mode.", {Distance = distance}) end local spamInterval = max(self.BaseSpamInterval * (1 - (speed / 100)), 0.03) if now - self.LastSpamTime >= spamInterval then self:AttemptParry() self.LastSpamTime = now AdvancedLog("ACTION", "Spam parry triggered.", {Distance = distance, SpamInterval = spamInterval}) end return elseif self.State == STATE_SPAM then self.State = STATE_TRACKING AdvancedLog("INFO", "Exiting SPAM mode.", {Distance = distance}) end -- Jika kecepatan bola sangat rendah, anggap bola berhenti if speed < 0.1 then if not self.BallStopped then self.BallStopped = true self.State = STATE_IDLE AdvancedLog("INFO", "Ball stopped.", {Speed = speed}) end return elseif self.BallStopped then self.BallStopped = false if self.State == STATE_IDLE then self.State = STATE_TRACKING AdvancedLog("INFO", "Ball resumed.", {Speed = speed}) end end -- Hitung curve angle (perubahan arah) antara frame sebelumnya dan saat ini local curveAngle = 0 if prevVelocity.Magnitude > 0 and ballVelocity.Magnitude > 0 then local dot = clamp(prevVelocity.Unit:Dot(ballVelocity.Unit), -1, 1) curveAngle = deg(acos(dot)) end AdvancedLog("DEBUG", "Ball metrics updated.", { Speed = speed, CurveAngle = curveAngle, Distance = distance, tImpact = tImpact, DynamicThreshold = dynamicThreshold, IncomingAngle = incomingAngle, State = self.State }) if tImpact < dynamicThreshold * 0.5 and self.State ~= STATE_ANTICIPATING then self.State = STATE_ANTICIPATING AdvancedLog("INFO", "Entering ANTICIPATING.", {tImpact = tImpact}) end if (self.State == STATE_TRACKING or self.State == STATE_ANTICIPATING) then if tImpact <= threshold or (curveAngle >= 15 and tImpact <= MAX_IMPACT_TIME) then self:AttemptParry() self.State = STATE_PARIED AdvancedLog("ACTION", "Parry triggered.", {tImpact = tImpact, CurveAngle = curveAngle}) end end if self.State == STATE_PARIED then local effectiveCooldown = self.OverpowerMode and self.OverpowerCooldown or self.CooldownTime if now - self.LastParryTime >= (threshold + effectiveCooldown) then self.State = STATE_COOLDOWN AdvancedLog("INFO", "Entering COOLDOWN.", {Elapsed = now - self.LastParryTime}) end end if self.State == STATE_COOLDOWN then local effectiveCooldownFinish = self.OverpowerMode and (self.OverpowerCooldown + 0.05) or (self.CooldownTime + 0.5) if now - self.LastParryTime >= (threshold + effectiveCooldownFinish) then self.State = STATE_TRACKING AdvancedLog("INFO", "Cooldown finished, resuming TRACKING.", nil) end end if curveAngle > 45 and tImpact < threshold then self.State = STATE_TRACKING AdvancedLog("INFO", "Reset parry due to sharp curve.", {CurveAngle = curveAngle}) end if distance <= 0 then AdvancedLog("WARN", "Invalid distance.", {Distance = distance}) end -- Update label tambahan dengan informasi penting (misal: ping & state) if self.AdditionalInfoLabel then local pingMS = math.floor(self:GetLocalPing() * 1000) self.AdditionalInfoLabel.Text = string.format("Ping: %d ms | State: %s", pingMS, self.State) end end ------------------------------- -- Main Update Loop ------------------------------- local controller = AdvancedParryController.new() controller:InitializeBallListener() controller:InitializeGUI() RunService.Heartbeat:Connect(function(dt) pcall(function() controller:UpdateState(dt) end) end)