local Config = { FOVSize = 180, HitPart = "Head", MaxFlightTicks = 100, FullDrawTicks = 20, } local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalPlayer = Players.LocalPlayer local Client = LocalPlayer:WaitForChild("PlayerScripts"):WaitForChild("Client") local Shared = ReplicatedStorage:WaitForChild("Shared") local BowController = require(Client:WaitForChild("BowController")) local CombatClient = require(Client:WaitForChild("CombatClient")) local FirstPersonAnimationModule = Client:WaitForChild("FirstPersonAnimation") local FirstPersonAnimation = require(FirstPersonAnimationModule) local TaCZMotion = require(FirstPersonAnimationModule:WaitForChild("TaCZMotion")) local BowMath = require(Shared:WaitForChild("BowMath")) local BowNet = require(Shared:WaitForChild("BowNet")) local BowConfig = require(Shared:WaitForChild("BowConfig")) local CharacterLife = require(Shared:WaitForChild("CharacterLife")) local ActiveRelease = nil local ActivePreview = nil local ActiveDrawingController = nil local CachedDraws = setmetatable({}, {__mode = "k"}) if Drawing and type(Drawing.new) == "function" then local FovCircle = Drawing.new("Circle") FovCircle.Position = UserInputService:GetMouseLocation() FovCircle.Visible = true FovCircle.Color = Color3.fromRGB(255, 255, 255) FovCircle.Radius = Config.FOVSize FovCircle.Transparency = 1 FovCircle.Filled = false FovCircle.NumSides = 64 RunService.PreRender:Connect(function() FovCircle.Position = UserInputService:GetMouseLocation() end) end local function FindTarget() local Camera = workspace.CurrentCamera if not Camera then return nil end local Mouse = UserInputService:GetMouseLocation() local BestPart = nil local BestDistance = Config.FOVSize local InfectionMode = LocalPlayer:GetAttribute("MatchMode") == "Infection" local function Consider(Character, Player) if not Character or Character.Name == "DeathModel" then return end if InfectionMode then local RoleSource = Player or Character if RoleSource:GetAttribute("InfectionRole") ~= "Zombie" then return end end if Player and Player:GetAttribute("MatchStatus") == "Dead" then return end local Humanoid = Character:FindFirstChildOfClass("Humanoid") if not Humanoid or CharacterLife.isDead(Humanoid) or Humanoid:GetState() == Enum.HumanoidStateType.Dead then return end local Part = Character:FindFirstChild(Config.HitPart) if not Part or not Part:IsA("BasePart") then return end local Screen = Camera:WorldToViewportPoint(Part.Position) if Screen.Z <= 0 then return end local Distance = (Vector2.new(Screen.X, Screen.Y) - Mouse).Magnitude if Distance >= BestDistance then return end BestDistance = Distance BestPart = Part end for _, Player in Players:GetPlayers() do if Player == LocalPlayer then continue end Consider(Player.Character, Player) end local Bots = workspace:FindFirstChild("Bots") if Bots then for _, Bot in Bots:GetChildren() do if not Bot:IsA("Model") then continue end Consider(Bot) end end return BestPart end local function SolveDirection(Controller, Origin, ChargeTicks, Target) local Seed = Controller.network and Controller.network.bowSeed local DrawId = Controller.drawId local Character = LocalPlayer.Character local Root = Character and Character:FindFirstChild("HumanoidRootPart") if not Seed or not DrawId or not Root or not Target or ChargeTicks == nil then return nil, "missing seed, draw ID, character root, target, or charge" end local Power = BowMath.powerTicks(ChargeTicks) if Power < 0.1 then return nil, "draw charge is below launch threshold" end local Effects = Controller.drawEffects local SpeedBonus = 1 + (Effects and Effects.ArrowSpeedBonus or 0) local Speed = BowMath.f32(Power * 3 * SpeedBonus) if Speed <= 0 then return nil, "launch speed is zero" end local Spread = BowMath.launchSpread(BowNet.random(Seed, DrawId)) local Source = BowMath.fromWorld(BowNet.clampVelocity(Root.AssemblyLinearVelocity) * BowConfig.Tick) local OriginBlocks = BowMath.fromWorld(Origin) local TargetVelocity = Target.AssemblyLinearVelocity local Drag = BowMath.AirInertia local function Candidate(Ticks) local Sum = (1 - Drag ^ Ticks) / (1 - Drag) local GravityDrop = 0.05 * (Ticks - Sum) / (1 - Drag) local Future = Target.Position + TargetVelocity * (Ticks * BowConfig.Tick) local Destination = BowMath.fromWorld(Future) local Delta = Vector3.new( Destination.x - OriginBlocks.x, Destination.y - OriginBlocks.y, Destination.z - OriginBlocks.z ) local RequiredVelocity = Vector3.new( Delta.X / Sum, (Delta.Y + GravityDrop) / Sum, Delta.Z / Sum ) local Aim = Vector3.new( (RequiredVelocity.X - Source.x) / Speed - Spread.x, RequiredVelocity.Y / Speed - Spread.y, (RequiredVelocity.Z - Source.z) / Speed - Spread.z ) return Aim, Aim.Magnitude - 1 end local PreviousTicks = 0.25 local _, PreviousError = Candidate(PreviousTicks) local BestAim = nil local BestTicks = nil local BestError = math.huge for Ticks = 0.5, Config.MaxFlightTicks, 0.5 do local Aim, Error = Candidate(Ticks) if math.abs(Error) < BestError then BestAim = Aim BestTicks = Ticks BestError = math.abs(Error) end if PreviousError > 0 and Error <= 0 then local Low, High = PreviousTicks, Ticks for _ = 1, 18 do local Mid = (Low + High) * 0.5 local _, MidError = Candidate(Mid) if MidError > 0 then Low = Mid else High = Mid end end Aim = Candidate((Low + High) * 0.5) if Aim.Magnitude > 0 then return Aim.Unit, nil, { FlightTicks = (Low + High) * 0.5, Power = Power, Speed = Speed, Spread = Spread, Source = Source, Fallback = false, } end return nil, "aim vector is zero" end PreviousTicks = Ticks PreviousError = Error end if not BestAim or BestAim.Magnitude <= 0 then return nil, "no usable trajectory" end return BestAim.Unit, nil, { FlightTicks = BestTicks, Power = Power, Speed = Speed, Spread = Spread, Source = Source, Fallback = true, Residual = BestError, } end local OldChargeTicks OldChargeTicks = hookfunction(BowNet.chargeTicks, function(...) local Ticks = OldChargeTicks(...) if ActiveRelease then ActiveRelease.ChargeTicks = Ticks end return Ticks end) local OldRandom OldRandom = hookfunction(BowNet.random, function(Seed, DrawId) local Controller = ActivePreview if not Controller or (type(Seed) == "number" and type(DrawId) == "number") then return OldRandom(Seed, DrawId) end local Cached = CachedDraws[Controller] local CurrentId = Controller.drawId if type(CurrentId) ~= "number" and Cached then CurrentId = Cached.Id end if type(Seed) == "number" and type(CurrentId) == "number" then DrawId = CurrentId Controller.previewDrawId = DrawId return OldRandom(Seed, DrawId) end Controller.trajectory:hide() return function() return 0.5 end end) local OldPreviewTrajectory OldPreviewTrajectory = hookfunction(BowController.previewTrajectory, function(Controller, ...) if Controller.drawing and (not Controller.network or type(Controller.network.bowSeed) ~= "number") then Controller.trajectory:hide() return end if Controller.drawing and type(Controller.drawId) ~= "number" then local Cached = CachedDraws[Controller] if Cached and type(Cached.Id) == "number" then Controller.drawId = Cached.Id else Controller.trajectory:hide() return end end local Previous = ActivePreview ActivePreview = Controller local Result = OldPreviewTrajectory(Controller, ...) ActivePreview = Previous return Result end) local OldLaunchPoint OldLaunchPoint = hookfunction(BowController.launchPoint, function(Controller, ...) local Origin, Direction = OldLaunchPoint(Controller, ...) local State = ActiveRelease local IsRelease = State and State.Controller == Controller local IsPreview = ActivePreview == Controller and Controller.drawing if not IsRelease and not IsPreview then return Origin, Direction end local Target = FindTarget() if not Target then return Origin, Direction end local ChargeTicks = if IsRelease then State.ChargeTicks else Controller.previewTicks local Aim = SolveDirection(Controller, Origin, ChargeTicks, Target) if Aim then if IsRelease then State.Direction = Aim State.Origin = Origin end return Origin, Aim end return Origin, Direction end) local OldSend OldSend = hookfunction(CombatClient.send, function(Network, Action, Params) local State = ActiveRelease local Matched = Action == "Release" and State and State.Controller.network == Network if Matched and State.Direction then Params.direction = State.Direction Params.origin = State.Origin end local Result = OldSend(Network, Action, Params) if Action == "Draw" and Network.controller then CachedDraws[Network.controller] = {Id = Result} end return Result end) local OldBeginDraw OldBeginDraw = hookfunction(BowController.beginDraw, function(Controller, ...) local WasDrawing = Controller.drawing local Result = OldBeginDraw(Controller, ...) if WasDrawing or not Controller.drawing then return Result end ActiveDrawingController = Controller return Result end) local OldAnimationUpdate OldAnimationUpdate = hookfunction(FirstPersonAnimation.update, function(Animation, Delta, Slot, Drawing, DrawTicks, ...) local Controller = ActiveDrawingController if Controller and Controller.firstPersonAnimation == Animation and Drawing and Slot == 1 then DrawTicks = math.max(DrawTicks, Config.FullDrawTicks) end return OldAnimationUpdate(Animation, Delta, Slot, Drawing, DrawTicks, ...) end) local OldMotionUpdate OldMotionUpdate = hookfunction(TaCZMotion.update, function(Motion, Delta, Drawing, DrawTicks, ...) local Result = OldMotionUpdate(Motion, Delta, Drawing, DrawTicks, ...) local Controller = ActiveDrawingController if Controller and Controller.firstPersonAnimation.tacz == Motion and Drawing and Motion.key == "tb23" then local Track = Motion.adsTrack if Track and Track.name == "pull_in" then Track.start = math.min(Track.start, Motion.clock - 0.2) end end return Result end) local OldRelease OldRelease = hookfunction(BowController.release, function(Controller, ...) local Previous = ActiveRelease local State = {Controller = Controller} ActiveRelease = State local Result = OldRelease(Controller, ...) ActiveRelease = Previous return Result end)