Add BladeBall/JustHub

This commit is contained in:
Apapapa 2025-02-05 21:56:10 +00:00
parent 13f4a5818a
commit 83de91fe82

449
BladeBall/JustHub Normal file
View File

@ -0,0 +1,449 @@
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)