--[[
Boulder Creek (place 120073741652089)
Animal ESP + silent aim.
How the game fires a shot (from the decompiled client):
Rifle.Fire
origin = Model.PrimaryPart.BarrelAttachment.WorldPosition
aimPoint= workspace:Raycast(Camera.CFrame.Position, Camera.CFrame.LookVector * 1500, ...)
dir = (aimPoint.Position - origin).Unit
projectileController:Fire(origin, dir, MuzzleVelocity / 0.28, userData)
ProjectileController.Fire(self, origin, dir, speed, userData, isReplicated, id)
local proj = caster:Fire(origin, dir, speed, userData, id) -- LOCAL simulation
if not isReplicated then
Remotes.ProjectileFire:FireServer(origin, dir.Unit * speed, id, userData)
end
The projectile is simulated on the client and the hit is reported by the client
(Remotes.ProjectileHitAnimal:FireServer), so the direction that matters is the one the
LOCAL simulation runs with. Wrapping ProjectileController.Fire/FireBatch replaces that
direction for the local simulation and for the server payload at once.
The ballistics are the game's own, copied from Ballistics.CalculateAcceleration and
Projectile.Step, so the solve is exact for this game rather than a generic parabola:
gravity = (0, -35.035717, 0)
accel(v) = -(0.6125 * |v*0.28|^2 * Cd * A) * (v*0.28).Unit / (mass*0.001)
* 3.5714285714 + gravity
step: a = accel(v); dp = v*dt + a*dt^2/2; v = v + a*dt; p = p + dp (dt = 1/60)
]]
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local LP = Players.LocalPlayer
local Camera = workspace.CurrentCamera
-- Re-running this file has to replace the previous copy. Each load installs its own hooks
-- and its own per-frame mod pass, and an older copy sitting there with mods off would quietly
-- undo the newer one's writes to the weapon every frame - which is exactly what happened
-- before this guard existed. The previous instance is torn down here first.
do
local previous = getgenv().BoulderOverlay
if type(previous) == "table" then
-- the library leaves its ScreenGui behind, so the old menu is removed with the old
-- instance's own reference rather than by guessing names
local ok = pcall(function()
local window = previous.STATE and previous.STATE.window
local frame = window and window.Items and window.Items.MainFrame
local gui = frame and frame.Instance and frame.Instance:FindFirstAncestorOfClass("ScreenGui")
if gui then gui:Destroy() end
end)
if type(previous.unload) == "function" then pcall(previous.unload) end
end
-- and sweep any copy a run that was killed before it could unload left behind: the menu
-- button is this file's own instance, so a ScreenGui holding one that is not this run's
-- is a dead copy of this overlay (five of them had piled up in gethui by the time this
-- was written, all stacked on the same spot)
pcall(function()
local holder = gethui()
if not holder then return end
for _, gui in ipairs(holder:GetChildren()) do
if gui:IsA("ScreenGui") then
for _, d in ipairs(gui:GetDescendants()) do
if d:IsA("TextButton") and d.Name == "BoulderMenuButton" and d.Text == "BC" then
gui:Destroy()
break
end
end
end
end
end)
end
-- ============================================================ state
local STATE = {
log = {},
tracks = {},
target = nil,
lastSolve = nil,
shotsSeen = 0,
redirected = 0,
hitsClaimed = 0,
lastError = nil,
aimHeld = true,
weapon = nil,
}
local function logEvent(kind, msg)
STATE.log[#STATE.log + 1] = { kind = kind, msg = tostring(msg), at = os.clock() }
if #STATE.log > 80 then table.remove(STATE.log, 1) end
end
-- set false by unload so every loop this file starts can retire itself
local RUN = { alive = true }
-- A load of this file that is replaced without its unload running (an executor re-exec, a
-- reload that overwrote the api table) would otherwise leave its main loop alive forever.
-- Each main loop that finds a different instance holding the api table retires itself, so a
-- stale copy can never sit there writing to the weapon alongside the live one.
local INSTANCE = {}
local function current()
if not RUN.alive then return false end
local now = getgenv().BoulderOverlay
if now and now.INSTANCE ~= nil and now.INSTANCE ~= INSTANCE then
RUN.alive = false
return false
end
return true
end
-- ============================================================ config
local CONFIG = {
aim = {
enabled = true,
fov = 25, -- degrees off the crosshair a target may sit
maxDistance = 1600, -- studs
wallCheck = true, -- require a clear line from the muzzle
lead = true, -- predict the animal's own travel
zone = "center", -- center | upper | lower
autoFire = false, -- reserved
},
esp = {
enabled = true,
animals = true,
dead = false,
mounts = false,
boxStyle = "Corners", -- Corners | Box | Both
name = true,
distance = true,
info = false,
textSize = 12,
fill = true,
maxDistance = 1600,
color = Color3.fromRGB(0, 214, 190),
deadColor = Color3.fromRGB(150, 150, 150),
playerColor = Color3.fromRGB(255, 90, 90),
thickness = 1,
},
mods = {
rapidFire = false,
fireRate = 900, -- rounds per minute written onto the live weapon
autoFire = false,
autoFireDelay = 0.10, -- seconds between trigger shots
infiniteAmmo = false,
},
}
-- ============================================================ ballistics (the game's own)
local GRAVITY = Vector3.new(0, -35.03571701049805, 0)
local STEP = 1 / 60
local function accel(v, mass, drag, area)
local s = v * 0.28
local mag = s.Magnitude
if mag <= 0 or mass <= 0 then return GRAVITY end
return -(0.6125 * mag * mag * drag * area) * s.Unit / (mass * 0.001)
* 3.571428571428571 + GRAVITY
end
local function stepProjectile(p, v, mass, drag, area, dt)
local a = accel(v, mass, drag, area)
local dp = v * dt + a * (dt * dt * 0.5)
return p + dp, v + a * dt
end
-- Time of flight for a projectile of `speed` launched from `origin` to reach a point that
-- is itself travelling at `vel`. Closed-form quadratic in t:
-- | P + V t - O |^2 = (speed t)^2
local function interceptTime(origin, pos, vel, speed)
local r = pos - origin
local a = vel:Dot(vel) - speed * speed
local b = 2 * r:Dot(vel)
local c = r:Dot(r)
if math.abs(a) < 1e-6 then
if math.abs(b) < 1e-6 then return nil end
local t = -c / b
return t > 0 and t or nil
end
local disc = b * b - 4 * a * c
if disc < 0 then return nil end
local sq = math.sqrt(disc)
local best
for _, t in ipairs({ (-b - sq) / (2 * a), (-b + sq) / (2 * a) }) do
if t > 0 and (not best or t < best) then best = t end
end
return best
end
-- Simulate a free-flight projectile (no collision) and report how close it came to a
-- moving point, and where. This is the same integrator the game runs, minus the raycasts,
-- so the miss it reports is the miss the hit test will see.
local function closestApproach(origin, dir, speed, mass, drag, area, tMax, targetFn)
local p, v = origin, dir * speed
local t = 0
local best, bestT, bestPos = math.huge, 0, p
while t < tMax do
local d = (p - targetFn(t)).Magnitude
if d < best then best, bestT, bestPos = d, t, p end
p, v = stepProjectile(p, v, mass, drag, area, STEP)
t = t + STEP
end
return best, bestT, bestPos
end
-- Solve the launch direction that puts the projectile through the target's path.
-- Seeded by the closed-form intercept, then corrected against the real trajectory:
-- fire, measure where the round actually passed the target, move the aim by that miss,
-- repeat. Four passes settle inside a fraction of a stud for every weapon in the game.
local function solveAim(origin, targetPos, targetVel, speed, mass, drag, area)
local function targetFn(t) return targetPos + targetVel * t end
local T = interceptTime(origin, targetPos, targetVel, speed) or 0.15
local aim = targetFn(T)
local dir = (aim - origin).Unit
for _ = 1, 4 do
local missDist, missT, missPos =
closestApproach(origin, dir, speed, mass, drag, area, math.max(T * 2.5, 0.35), targetFn)
local missVec = targetFn(missT) - missPos
aim = aim + missVec
dir = (aim - origin).Unit
T = missT
end
return dir, T
end
-- rotate direction `d` by the rotation that takes `from` to `to` (keeps pellet spread)
local function rotateDir(from, to, d)
local a = CFrame.lookAt(Vector3.zero, from)
local b = CFrame.lookAt(Vector3.zero, to)
return b * (a:Inverse() * d)
end
-- ============================================================ animal tracking
local ANIMAL_FOLDERS = {
{ folder = "Animals", kind = "animal" },
{ folder = "DeadAnimals", kind = "dead" },
{ folder = "Mounts", kind = "mount" },
{ folder = "Dogs", kind = "dog" },
}
local function bodyPart(model)
local pb = model:FindFirstChild("_ProjectileBounds")
if pb and pb:IsA("BasePart") then return pb end
local root = model:FindFirstChild("RootPart")
if root and root:IsA("BasePart") then return root end
return model.PrimaryPart or model:FindFirstChildWhichIsA("BasePart")
end
-- The point a shot should steer at: the middle of the volume the bullet has to pass
-- through, lifted or dropped onto the requested zone.
local function aimPointOf(model, part)
if not part then return nil end
local p = part.Position
local size = part:IsA("BasePart") and part.Size or model:GetExtentsSize()
local f = CONFIG.aim.zone
if f == "upper" then
p = p + Vector3.new(0, size.Y * 0.30, 0)
elseif f == "lower" then
p = p - Vector3.new(0, size.Y * 0.30, 0)
end
return p
end
local function labelFor(model, kind)
local display = model:GetAttribute("DisplayName")
if display then return display end
return model.Name
end
local function refreshTracks()
local now = os.clock()
local seen = {}
for _, entry in ipairs(ANIMAL_FOLDERS) do
local folder = workspace:FindFirstChild(entry.folder)
if folder then
for _, model in ipairs(folder:GetChildren()) do
if model:IsA("Model") then
local part = bodyPart(model)
if part then
seen[model] = true
local rec = STATE.tracks[model]
local pos = part.Position
if rec then
local dt = now - rec.at
if dt > 0.0001 then
local inst = (pos - rec.pos) / dt
-- exponential smoothing: the raw per-frame delta on a
-- replicated animal is noisy enough to overshoot a lead
rec.vel = rec.vel + (inst - rec.vel) * math.min(dt * 8, 0.5)
end
rec.pos, rec.at = pos, now
else
rec = { pos = pos, vel = Vector3.zero, at = now, kind = entry.kind }
STATE.tracks[model] = rec
end
rec.model = model
rec.part = part
rec.kind = entry.kind
rec.name = labelFor(model, entry.kind)
rec.aimPos = aimPointOf(model, part) or pos
end
end
end
end
end
for model in pairs(STATE.tracks) do
if not seen[model] or not model.Parent then
STATE.tracks[model] = nil
end
end
end
-- ============================================================ target selection
local function trackAllowed(rec)
if rec.kind == "animal" then return CONFIG.esp.animals end
if rec.kind == "dead" then return CONFIG.esp.dead end
if rec.kind == "mount" then return CONFIG.esp.mounts end
if rec.kind == "dog" then return CONFIG.esp.mounts end
return false
end
local function hasLine(origin, target, model)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
local ignore = { model, LP.Character, Camera }
for _, name in ipairs({ "Projectiles", "Debug", "DebugLaser", "Barriers", "LocationRegions" }) do
local inst = workspace:FindFirstChild(name)
if inst then table.insert(ignore, inst) end
end
params.FilterDescendantsInstances = ignore
local hit = workspace:Raycast(origin, target - origin, params)
return hit == nil
end
local function selectTarget(origin)
local camPos = Camera.CFrame.Position
local look = Camera.CFrame.LookVector
local cosFov = math.cos(math.rad(math.clamp(CONFIG.aim.fov, 1, 179)))
local best, bestScore
for model, rec in pairs(STATE.tracks) do
if rec.kind == "animal" and model.Parent then
local delta = rec.aimPos - camPos
local dist = delta.Magnitude
if dist > 0.5 and dist <= CONFIG.aim.maxDistance then
local cosA = look:Dot(delta / dist)
if cosA >= cosFov and (not best or cosA > bestScore) then
-- the wall is checked per candidate so a blocked animal cannot shadow a
-- clear one just because it sits closer to the crosshair
if not CONFIG.aim.wallCheck or hasLine(origin, rec.aimPos, model) then
best, bestScore = rec, cosA
end
end
end
end
end
return best
end
-- ============================================================ the solver the fire hook calls
local function aimDirection(origin, fallbackDir, speed, userData)
if not CONFIG.aim.enabled or not STATE.aimHeld then return fallbackDir end
local rec = selectTarget(origin)
if not rec then
STATE.target = nil
return fallbackDir
end
local mass, drag, area = 0, 0, 0
local props = userData and userData.BulletProperties
if props then
mass = tonumber(props.Mass) or 0
drag = tonumber(props.DragCoefficient) or 0
end
area = tonumber(userData and userData.CrossSectionalArea) or 0
if mass <= 0 or drag <= 0 or area <= 0 then
-- no usable bullet data: straight line to the lead point is still a hit
local vel = CONFIG.aim.lead and rec.vel or Vector3.zero
local T = interceptTime(origin, rec.aimPos, vel, speed) or 0.15
return ((rec.aimPos + vel * T) - origin).Unit
end
local vel = CONFIG.aim.lead and rec.vel or Vector3.zero
local dir, T = solveAim(origin, rec.aimPos, vel, speed, mass, drag, area)
STATE.target = rec
STATE.lastSolve = {
model = rec.model, dir = dir, flight = T, speed = speed,
dist = (rec.aimPos - origin).Magnitude, vel = vel,
}
logEvent("solve", string.format("%s %.0f studs flight %.3fs lead %.1f studs",
rec.name, (rec.aimPos - origin).Magnitude, T, (vel * T).Magnitude))
return dir
end
-- ============================================================ the hook
local ProjectileController = require(ReplicatedStorage.ReservesCommon.Client.Controllers.ProjectileController)
local originalFire, originalFireBatch
local function fireHook(self, origin, direction, speed, userData, isReplicated, ...)
if not isReplicated then
STATE.shotsFired = (STATE.shotsFired or 0) + 1
local ok, result = pcall(aimDirection, origin, direction, speed, userData)
if ok and typeof(result) == "Vector3" and result.Magnitude > 0 then
direction = result
STATE.redirected = STATE.redirected + 1
elseif not ok then
STATE.lastError = tostring(result)
end
end
return originalFire(self, origin, direction, speed, userData, isReplicated, ...)
end
local function fireBatchHook(self, origin, directions, speed, userData, ids, isReplicated, ...)
if not isReplicated and type(directions) == "table" and #directions > 0 then
STATE.shotsFired = (STATE.shotsFired or 0) + 1
local ok, solved = pcall(aimDirection, origin, directions[1], speed, userData)
if ok and typeof(solved) == "Vector3" and solved.Magnitude > 0 then
-- keep the shell's spread pattern: rotate every pellet by the same rotation
-- that moves the centre pellet onto the target
local out = {}
for i, d in ipairs(directions) do
out[i] = rotateDir(directions[1], solved, d)
end
directions = out
STATE.redirected = STATE.redirected + 1
elseif not ok then
STATE.lastError = tostring(solved)
end
end
return originalFireBatch(self, origin, directions, speed, userData, ids, isReplicated, ...)
end
originalFire = ProjectileController.Fire
originalFireBatch = ProjectileController.FireBatch
ProjectileController.Fire = fireHook
ProjectileController.FireBatch = fireBatchHook
-- Kills are read off the world instead of off a remote: a dead animal is moved into
-- workspace.DeadAnimals, so that folder growing is a kill the shot landed. Reading it
-- costs nothing and never touches a remote or a namecall.
do
local deadCount = #workspace.DeadAnimals:GetChildren()
task.spawn(function()
while current() do
task.wait(0.5)
local now = #workspace.DeadAnimals:GetChildren()
if now > deadCount then
local gained = now - deadCount
STATE.hitsClaimed = STATE.hitsClaimed + gained
logEvent("kill", string.format("dead animals +%d (total %d)", gained, STATE.hitsClaimed))
end
deadCount = now
end
end)
end
-- ============================================================ gun mods
-- The client builds one weapon object per equipped gun, and the Rifle class covers every
-- firearm class in the game (its own Configuration.Class is "Rifle" or "Shotgun"). That
-- object owns the fire-rate wait, the magazine and the trigger, so it is the thing to hold
-- on to: mods are written straight onto the live gun the game is using, not a copy.
local RifleClass = require(ReplicatedStorage.ReservesCommon.Equipment.Rifle)
local function captureWeapon(w)
if type(w) ~= "table" or not w._lifetime then return end
if not w._stockKnown then
-- remembered per weapon, nil included: every bolt-action and pump-action gun has no
-- FireRate at all, so "no cooldown" has to be restorable as well as "600 rpm"
w._stockKnown = true
w._stockFireRate = w.FireRate
w._stockLoadBetweenRounds = w.LoadBetweenRounds and true or false
end
if STATE.weapon ~= w then
STATE.weapon = w
logEvent("mods", "weapon captured: " .. tostring(w.DisplayName or "?"))
end
end
-- Two ways in, because a gun that was already at the shoulder when this loaded would never
-- be seen by a constructor hook: the constructor catches equips from here on, and every
-- method the game calls on the object catches the one already in hand (aiming, reloading
-- and firing all resolve through the class, so the first interaction with the gun hands it
-- over).
local originalRifleNew = RifleClass.new
RifleClass.new = function(...)
local weapon = originalRifleNew(...)
captureWeapon(weapon)
return weapon
end
local originalMethods = {}
print("[diag] loop enter", type(ipairs), type(RifleClass))
local diagNames = { "Fire", "Reload", "Load", "SetAimIntent", "ClearAimIntent", "CanAim", "IsReady" }
print("[diag] names", #diagNames, tostring(diagNames[1]), tostring(diagNames[7]))
for _, name in ipairs(diagNames) do
print("[diag] iter", type(name), tostring(name), type(RifleClass[name]))
local original = RifleClass[name]
if type(original) == "function" then
originalMethods[name] = original
RifleClass[name] = function(self, ...)
captureWeapon(self)
return original(self, ...)
end
print("[diag] wrapped", tostring(name), debug.info(RifleClass[name],"s"))
end
end
-- The gun already in hand when this loaded is not reachable through the class, but the
-- viewmodel that owns it is: its render step runs on a closure whose first upvalue is the
-- viewmodel object, and that object carries CurrentItem - the live weapon. Reading it costs
-- nothing and means the mods are on from the first shot rather than the second.
local function findEquippedWeapon()
local ok, connections = pcall(getconnections, RunService.RenderStepped)
if not ok or type(connections) ~= "table" then return nil end
for _, connection in ipairs(connections) do
local okInfo, source = pcall(function() return debug.info(connection.Function, "s") end)
if okInfo and tostring(source):find("Equipment.Viewmodel") then
local okUp, a, b = pcall(debug.getupvalue, connection.Function, 1)
if okUp then
local viewmodel = type(a) == "string" and b or a
if type(viewmodel) == "table" and viewmodel.CurrentItem then
return viewmodel.CurrentItem
end
end
end
end
return nil
end
do
local weapon = findEquippedWeapon()
if weapon then captureWeapon(weapon) end
end
local lastTrigger = 0
local function applyMods()
local w = STATE.weapon
-- diagnostics, sampled rather than rebuilt every frame
if not STATE.modSeen or os.clock() - (STATE.modSeenAt or 0) > 0.5 then
STATE.modSeenAt = os.clock()
STATE.modSeen = {
hasWeapon = w ~= nil,
alive = w ~= nil and (w._lifetime ~= nil and w._lifetime.Alive),
rapid = CONFIG.mods.rapidFire,
rate = CONFIG.mods.fireRate,
ammo = CONFIG.mods.infiniteAmmo,
weaponRate = w and w.FireRate,
weaponLBR = w and w.LoadBetweenRounds,
loaded = w and w.Loaded,
}
end
if w and not (w._lifetime and w._lifetime.Alive) then
-- unequipped since it was captured: drop it so the next gun takes over rather than
-- the mods writing to an object the game has finished with
STATE.weapon = nil
w = nil
end
if not w then
-- nothing in hand: look for an equipped gun every second rather than every frame
local now = os.clock()
if not STATE.weaponLook or now - STATE.weaponLook > 1 then
STATE.weaponLook = now
local found = findEquippedWeapon()
if found then
captureWeapon(found)
w = STATE.weapon
end
end
if not w then return end
end
-- Every bolt-action and pump-action gun in the game ships FireRate = nil, so there is no
-- cooldown to shorten: the rate is the bolt/pump cycle. Fire() ends with
-- Loaded = not LoadBetweenRounds, and the next shot pays for that by running Load(),
-- which waits out the bolt animation. Holding LoadBetweenRounds down and Loaded up
-- removes the cycle, and FireRate (where the gun has one) is raised for the cooldown.
if CONFIG.mods.rapidFire then
if w.FireRate ~= CONFIG.mods.fireRate then w.FireRate = CONFIG.mods.fireRate end
if w.LoadBetweenRounds then w.LoadBetweenRounds = false end
if not w.Loaded then w.Loaded = true end
if w.NeedsLoading then w.NeedsLoading = false end
elseif w._stockKnown then
if w.FireRate ~= w._stockFireRate then w.FireRate = w._stockFireRate end
if w.LoadBetweenRounds ~= w._stockLoadBetweenRounds then
w.LoadBetweenRounds = w._stockLoadBetweenRounds
end
end
-- Fire drains the magazine one round at a time and reloads when it reaches zero. Holding
-- it at capacity means the reload path is never entered; the reserve is left alone.
if CONFIG.mods.infiniteAmmo then
local cap = w.MagazineCapacity or 0
if (w.BulletsInMagazine or 0) < cap then
w.BulletsInMagazine = cap
w.NeedsReloading = false
end
end
-- Fire only runs while the gun is at the shoulder (its first check is Aiming), so the
-- trigger releases only when a target is in the aim cone and the gun is up.
if CONFIG.mods.autoFire and w.Aiming and not w.Reloading and not w._viewmodel.Debounce then
if os.clock() - lastTrigger >= CONFIG.mods.autoFireDelay then
if selectTarget(Camera.CFrame.Position) then
lastTrigger = os.clock()
task.spawn(function()
local ok, err = pcall(function() w:Fire() end)
if not ok then STATE.lastError = tostring(err) end
end)
end
end
end
end
-- ============================================================ ESP
local espGui, espHolder
do
local function container()
local ok, hui = pcall(gethui)
if ok and typeof(hui) == "Instance" then return hui end
local ok2, core = pcall(function() return game:GetService("CoreGui") end)
if ok2 and core then return core end
return LP:FindFirstChildOfClass("PlayerGui")
end
espGui = Instance.new("ScreenGui")
espGui.Name = "BoulderEsp"
espGui.ResetOnSpawn = false
espGui.IgnoreGuiInset = true
espGui.DisplayOrder = 100
espGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
pcall(function() espGui.Parent = container() end)
espHolder = Instance.new("Folder")
espHolder.Name = "targets"
espHolder.Parent = espGui
end
STATE.drawings = {}
-- One drawing per animal, owned by this table. The previous version only ever hid a
-- drawing while its model was still being tracked, so an animal that despawned left its
-- box painted on the screen for the rest of the match. Now a drawing whose model is gone
-- is freed in the same pass that draws the rest.
local function newDrawing(model)
local box = Instance.new("Frame")
box.Name = "box"
box.BackgroundTransparency = 1
box.BorderSizePixel = 0
box.Visible = false
box.Parent = espHolder
local sides, corners = {}, {}
for i = 1, 4 do
local line = Instance.new("Frame")
line.Name = "side" .. i
line.BorderSizePixel = 0
line.Parent = box
sides[i] = line
end
for i = 1, 8 do
local line = Instance.new("Frame")
line.Name = "corner" .. i
line.BorderSizePixel = 0
line.Parent = box
corners[i] = line
end
local label = Instance.new("TextLabel")
label.Name = "name"
label.BackgroundTransparency = 1
label.Font = Enum.Font.GothamMedium
label.TextSize = 12
label.RichText = true
label.TextXAlignment = Enum.TextXAlignment.Center
label.TextYAlignment = Enum.TextYAlignment.Bottom
label.TextStrokeTransparency = 0.55
label.TextStrokeColor3 = Color3.new(0, 0, 0)
label.Visible = false
label.Parent = espHolder
local highlight = Instance.new("Highlight")
highlight.FillTransparency = 0.82
highlight.OutlineTransparency = 0.35
highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
highlight.Enabled = false
highlight.Adornee = model
highlight.Parent = espHolder
return { box = box, sides = sides, corners = corners, label = label, highlight = highlight }
end
local function freeDrawing(dr)
if dr.box then dr.box:Destroy() end
if dr.label then dr.label:Destroy() end
if dr.highlight then dr.highlight:Destroy() end
end
local function drawingFor(model)
local dr = STATE.drawings[model]
if dr and dr.box and dr.box.Parent and dr.label and dr.label.Parent then return dr end
dr = newDrawing(model)
STATE.drawings[model] = dr
return dr
end
-- Screen-space box of the volume a bullet has to pass through. The centre has to be in
-- front of the camera, otherwise a corner behind the near plane stretches the box across
-- the whole screen.
local function projectBox(model, rec)
local part = rec.part
if not (part and part.Parent) then return nil end
local cf, size
if part:IsA("BasePart") then
cf, size = part.CFrame, part.Size
else
local ok, c, s = pcall(function() return model:GetBoundingBox() end)
if not ok or not c then return nil end
cf, size = c, s
end
local centre = Camera:WorldToViewportPoint(cf.Position)
if centre.Z <= 0 then return nil end
local minX, minY = math.huge, math.huge
local maxX, maxY = -math.huge, -math.huge
local any = false
for ox = -1, 1, 2 do
for oy = -1, 1, 2 do
for oz = -1, 1, 2 do
local world = cf * Vector3.new(ox * size.X * 0.5, oy * size.Y * 0.5, oz * size.Z * 0.5)
local sp = Camera:WorldToViewportPoint(world)
if sp.Z > 0 then
any = true
minX, maxX = math.min(minX, sp.X), math.max(maxX, sp.X)
minY, maxY = math.min(minY, sp.Y), math.max(maxY, sp.Y)
end
end
end
end
if not any or maxX - minX < 0.5 or maxY - minY < 0.5 then return nil end
return minX, minY, maxX, maxY
end
local function colourFor(rec)
if rec.kind == "dead" then return CONFIG.esp.deadColor end
if rec.kind == "mount" or rec.kind == "dog" then return Color3.fromRGB(240, 190, 90) end
return CONFIG.esp.color
end
local nameCache = {}
local function prettyName(model)
local cached = nameCache[model.Name]
if cached then return cached end
local out = model:GetAttribute("DisplayName")
if not out or out == "" then
out = tostring(model.Name):gsub("(%l)(%u)", "%1 %2")
end
nameCache[model.Name] = out
return out
end
local function drawEsp()
local drawn = {}
local camPos = Camera.CFrame.Position
if CONFIG.esp.enabled then
for model, rec in pairs(STATE.tracks) do
if rec.part and rec.part.Parent and model.Parent and trackAllowed(rec) then
local dist = (rec.aimPos - camPos).Magnitude
if dist <= CONFIG.esp.maxDistance then
local minX, minY, maxX, maxY = projectBox(model, rec)
if minX then
drawn[model] = true
local dr = drawingFor(model)
local col = colourFor(rec)
local w, h = maxX - minX, maxY - minY
local t = CONFIG.esp.thickness
-- fade with range so a valley full of animals stays readable up close
local fade = math.clamp((dist / math.max(CONFIG.esp.maxDistance, 1)) * 0.55, 0, 0.55)
local style = CONFIG.esp.boxStyle
local showBox = style ~= "Corners"
local showCorners = style ~= "Box"
dr.box.Visible = true
dr.box.Position = UDim2.fromOffset(minX, minY)
dr.box.Size = UDim2.fromOffset(math.max(w, 1), math.max(h, 1))
local sideRects = {
{ 0, 0, w, t }, { 0, h - t, w, t }, { 0, 0, t, h }, { w - t, 0, t, h },
}
for i, s in ipairs(sideRects) do
local line = dr.sides[i]
line.Visible = showBox
line.BackgroundColor3 = col
line.BackgroundTransparency = fade
line.Position = UDim2.fromOffset(s[1], s[2])
line.Size = UDim2.fromOffset(s[3], s[4])
end
local cw = math.clamp(w * 0.22, 3, 11)
local ch = math.clamp(h * 0.22, 3, 11)
local cornerRects = {
{ 0, 0, cw, t }, { 0, 0, t, ch },
{ w - cw, 0, cw, t }, { w - t, 0, t, ch },
{ 0, h - t, cw, t }, { 0, h - ch, t, ch },
{ w - cw, h - t, cw, t }, { w - t, h - ch, t, ch },
}
for i, s in ipairs(cornerRects) do
local line = dr.corners[i]
line.Visible = showCorners
line.BackgroundColor3 = col
line.BackgroundTransparency = fade
line.Position = UDim2.fromOffset(s[1], s[2])
line.Size = UDim2.fromOffset(s[3], s[4])
end
dr.highlight.Enabled = CONFIG.esp.fill
dr.highlight.FillColor = col
dr.highlight.OutlineColor = col
dr.highlight.FillTransparency = math.clamp(0.74 + fade * 0.4, 0, 0.95)
dr.highlight.OutlineTransparency = math.clamp(0.25 + fade, 0, 0.95)
local bits = {}
if CONFIG.esp.name then
bits[#bits + 1] = string.format("%s", prettyName(model))
end
if CONFIG.esp.info and rec.kind == "animal" then
local variant = model:GetAttribute("Variant")
local sex = model:GetAttribute("Sex")
local extra = {}
if variant and variant ~= "Default" then extra[#extra + 1] = tostring(variant) end
if sex then extra[#extra + 1] = tostring(sex) end
if #extra > 0 then bits[#bits + 1] = table.concat(extra, " ") end
end
if CONFIG.esp.distance then
bits[#bits + 1] = string.format("%dm",
math.floor(dist * 0.28))
end
local text = table.concat(bits, " ")
dr.label.Visible = text ~= ""
dr.label.Text = text
dr.label.TextColor3 = col
dr.label.TextSize = CONFIG.esp.textSize
dr.label.TextTransparency = fade * 0.5
dr.label.Position = UDim2.fromOffset(minX - 60, minY - 17)
dr.label.Size = UDim2.fromOffset(w + 120, 16)
end
end
end
end
end
-- Whatever was not drawn this frame is either hidden (still alive, off screen or out of
-- range) or freed (the animal is gone). Nothing can be left stranded on the screen.
for model, dr in pairs(STATE.drawings) do
if not drawn[model] then
if not model.Parent or not STATE.tracks[model] then
freeDrawing(dr)
STATE.drawings[model] = nil
else
dr.box.Visible = false
dr.label.Visible = false
dr.highlight.Enabled = false
end
end
end
end
-- ============================================================ main loops
local mainConnection
mainConnection = RunService.RenderStepped:Connect(function()
if not current() then
if mainConnection then mainConnection:Disconnect() end
mainConnection = nil
return
end
local ok, err = pcall(function()
refreshTracks()
drawEsp()
applyMods()
end)
if not ok then
STATE.lastError = tostring(err)
if not STATE.loopErrLogged or os.clock() - STATE.loopErrLogged > 3 then
STATE.loopErrLogged = os.clock()
logEvent("error", tostring(err))
end
end
end)
-- ============================================================ interface (same library as trident)
local uiRefresh
do
local Library = loadstring(game:HttpGet("https://pastebin.com/raw/ncKGkPTW"))()
Library.Folders = { Directory = "boulder", Configs = "boulder/Configs", Assets = "boulder/Assets" }
for _, Folder in pairs(Library.Folders) do
if not isfolder(Folder) then makefolder(Folder) end
end
pcall(function() Library:ChangeTheme("Accent", Color3.fromRGB(0, 190, 180)) end)
local Window = Library:Window({ Name = "Boulder Creek", MobileButtonText = "BC" })
STATE.window = Window
-- The library only builds its hide/show button when the Settings toggle's callback
-- runs, so on mobile the menu can end up with nothing to open it. This is that button,
-- made here and always present: tap to open/close, drag to move it where the thumb
-- rests, and it stays put between matches.
do
local button = Instance.new("TextButton")
button.Name = "BoulderMenuButton"
button.Text = "BC"
button.Font = Enum.Font.GothamBold
button.TextSize = 19
button.AutoButtonColor = false
button.BorderSizePixel = 2
button.BackgroundColor3 = Color3.fromRGB(17, 21, 27)
button.BorderColor3 = Color3.fromRGB(0, 190, 180)
button.TextColor3 = Color3.fromRGB(236, 239, 243)
button.Size = UDim2.fromOffset(46, 46)
button.Position = UDim2.new(1, -62, 0, 90)
button.ZIndex = 5000
button.Active = true
button.Parent = espGui
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(1, 0)
corner.Parent = button
STATE.menuButton = button
local dragging, dragStart, startPos, moved = false, nil, nil, 0
button.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1
or input.UserInputType == Enum.UserInputType.Touch then
dragging, dragStart, startPos, moved = true, input.Position, button.Position, 0
button.BackgroundColor3 = Color3.fromRGB(24, 30, 37)
end
end)
button.InputChanged:Connect(function(input)
if not dragging then return end
if input.UserInputType == Enum.UserInputType.MouseMovement
or input.UserInputType == Enum.UserInputType.Touch then
local delta = input.Position - dragStart
moved = math.max(moved, delta.Magnitude)
button.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X,
startPos.Y.Scale, startPos.Y.Offset + delta.Y)
end
end)
button.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1
and input.UserInputType ~= Enum.UserInputType.Touch then return end
dragging = false
button.BackgroundColor3 = Color3.fromRGB(17, 21, 27)
-- a drag that ends on the button is not a tap: only a near-still press opens the menu
if moved < 10 and STATE.window then
STATE.window:SetOpen(not STATE.window.IsOpen)
end
end)
end
local Controls = {}
local function track(Element, Get)
Controls[#Controls + 1] = { Element = Element, Get = Get }
return Element
end
uiRefresh = function()
for _, e in ipairs(Controls) do
pcall(function() e.Element:Set(e.Get()) end)
end
end
local function Section(Page, Name, Side) return Page:Section({ Name = Name, Side = Side }) end
local function Toggle(Parent, Name, Flag, Get, Set)
return track(Parent:Toggle({
Name = Name, Flag = Flag, Default = Get(),
Callback = function(Value) Set(Value) end,
}), Get)
end
local function Slider(Parent, Name, Flag, Min, Max, Get, Set, Suffix)
return track(Parent:Slider({
Name = Name, Flag = Flag, Min = Min, Max = Max, Decimals = 1,
Suffix = Suffix or "", Default = Get(),
Callback = function(Value) Set(Value) end,
}), Get)
end
local function Dropdown(Parent, Name, Flag, Items, Get, Set)
return track(Parent:Dropdown({
Name = Name, Flag = Flag, Items = Items, Default = Get(),
Callback = function(Value) Set(Value) end,
}), Get)
end
local Aimbot = Window:Page({ Name = "Silent aim", Columns = 2 })
local A1 = Section(Aimbot, "Target", 1)
Toggle(A1, "Enable silent aim", "A_enabled", function() return CONFIG.aim.enabled end,
function(v) CONFIG.aim.enabled = v end)
Slider(A1, "Aim FOV", "A_fov", 1, 180, function() return CONFIG.aim.fov end,
function(v) CONFIG.aim.fov = v end, " deg")
Slider(A1, "Max distance", "A_max", 100, 3000, function() return CONFIG.aim.maxDistance end,
function(v) CONFIG.aim.maxDistance = v end, " studs")
Toggle(A1, "Wall check", "A_wall", function() return CONFIG.aim.wallCheck end,
function(v) CONFIG.aim.wallCheck = v end)
Toggle(A1, "Lead moving animals", "A_lead", function() return CONFIG.aim.lead end,
function(v) CONFIG.aim.lead = v end)
Dropdown(A1, "Aim zone", "A_zone", { "Center", "Upper", "Lower" },
function() return CONFIG.aim.zone:sub(1, 1):upper() .. CONFIG.aim.zone:sub(2) end,
function(v) CONFIG.aim.zone = string.lower(v) end)
local A2 = Section(Aimbot, "Status", 2)
Toggle(A2, "Aim active", "A_held", function() return STATE.aimHeld end,
function(v) STATE.aimHeld = v end)
local StatusShots = A2:Label("shots redirected: -")
local StatusTarget = A2:Label("last target: -")
task.spawn(function()
while current() do
task.wait(0.25)
pcall(function()
StatusShots:SetText("shots redirected: " .. tostring(STATE.redirected))
local t = STATE.lastSolve
StatusTarget:SetText("last target: " .. (t and string.format("%s %.0f studs", t.model and t.model.Name or "?", t.dist) or "-"))
end)
end
end)
local Visuals = Window:Page({ Name = "Visuals", Columns = 2 })
local V1 = Section(Visuals, "ESP", 1)
Toggle(V1, "Enable ESP", "E_enabled", function() return CONFIG.esp.enabled end,
function(v) CONFIG.esp.enabled = v end)
Dropdown(V1, "Box style", "E_style", { "Corners", "Box", "Both" },
function() return CONFIG.esp.boxStyle end,
function(v) CONFIG.esp.boxStyle = v end)
Toggle(V1, "Name", "E_name", function() return CONFIG.esp.name end,
function(v) CONFIG.esp.name = v end)
Toggle(V1, "Distance", "E_dist", function() return CONFIG.esp.distance end,
function(v) CONFIG.esp.distance = v end)
Toggle(V1, "Info (sex / variant)", "E_info", function() return CONFIG.esp.info end,
function(v) CONFIG.esp.info = v end)
Toggle(V1, "Fill (highlight)", "E_fill", function() return CONFIG.esp.fill end,
function(v) CONFIG.esp.fill = v end)
local V2 = Section(Visuals, "Targets", 2)
Toggle(V2, "Alive animals", "E_animals", function() return CONFIG.esp.animals end,
function(v) CONFIG.esp.animals = v end)
Toggle(V2, "Dead animals", "E_dead", function() return CONFIG.esp.dead end,
function(v) CONFIG.esp.dead = v end)
Toggle(V2, "Mounts / dogs", "E_mounts", function() return CONFIG.esp.mounts end,
function(v) CONFIG.esp.mounts = v end)
Slider(V2, "Max distance", "E_max", 100, 3000, function() return CONFIG.esp.maxDistance end,
function(v) CONFIG.esp.maxDistance = v end, " studs")
Slider(V2, "Box thickness", "E_thick", 1, 4, function() return CONFIG.esp.thickness end,
function(v) CONFIG.esp.thickness = v end, " px")
Slider(V2, "Label size", "E_text", 9, 18, function() return CONFIG.esp.textSize end,
function(v) CONFIG.esp.textSize = v end, " px")
track(V2:Label("Colour"):Colorpicker({
Flag = "E_colour", Default = CONFIG.esp.color,
Callback = function(v) CONFIG.esp.color = v end,
}), function() return CONFIG.esp.color end)
local Misc = Window:Page({ Name = "Menu", Columns = 2 })
local M1 = Section(Misc, "Interface", 1)
Toggle(M1, "Mobile open/close button", "M_button",
function() return STATE.menuButton and STATE.menuButton.Visible end,
function(v)
if STATE.menuButton then STATE.menuButton.Visible = v end
end)
local M2 = Section(Misc, "Session", 2)
M2:Label("Unload removes the hooks and the ESP.")
local Mods = Window:Page({ Name = "Gun mods", Columns = 2 })
local G1 = Section(Mods, "Fire", 1)
Toggle(G1, "Rapid fire", "G_rapid", function() return CONFIG.mods.rapidFire end,
function(v) CONFIG.mods.rapidFire = v end)
Slider(G1, "Fire rate", "G_rate", 120, 3000, function() return CONFIG.mods.fireRate end,
function(v) CONFIG.mods.fireRate = v end, " rpm")
Toggle(G1, "Auto fire (while aiming)", "G_auto", function() return CONFIG.mods.autoFire end,
function(v) CONFIG.mods.autoFire = v end)
Slider(G1, "Auto-fire delay", "G_delay", 0.02, 1, function() return CONFIG.mods.autoFireDelay end,
function(v) CONFIG.mods.autoFireDelay = v end, " s")
local G2 = Section(Mods, "Ammo", 2)
Toggle(G2, "Infinite ammo", "G_ammo", function() return CONFIG.mods.infiniteAmmo end,
function(v) CONFIG.mods.infiniteAmmo = v end)
local GunStatus = G2:Label("weapon: -")
task.spawn(function()
while current() do
task.wait(0.5)
pcall(function()
local w = STATE.weapon
if w and w._lifetime and w._lifetime.Alive then
GunStatus:SetText(string.format("%s | mag %d/%d | %.0f rpm | %s",
tostring(w.DisplayName or "?"), math.floor(w.BulletsInMagazine or 0),
math.floor(w.MagazineCapacity or 0), w.FireRate or 0,
w.Aiming and "aiming" or "hip"))
else
GunStatus:SetText("weapon: -")
end
end)
end
end)
end
-- a keybind that opens and closes the menu, same as the mobile button
do
local UIS = game:GetService("UserInputService")
UIS.InputBegan:Connect(function(input, processed)
if processed then return end
local key = input.KeyCode
if key == Enum.KeyCode.RightShift or key == Enum.KeyCode.Insert then
if STATE.window then STATE.window:SetOpen(not STATE.window.IsOpen) end
end
end)
end
-- ============================================================ api
getgenv().BoulderOverlay = {
INSTANCE = INSTANCE,
CONFIG = CONFIG,
STATE = STATE,
solve = solveAim,
interceptTime = interceptTime,
accel = accel,
tracking = function() return STATE.tracks end,
weapon = function() return STATE.weapon end,
mods = function()
local w = STATE.weapon
return {
captured = w ~= nil,
name = w and w.DisplayName or nil,
fireRate = w and w.FireRate or nil,
stockFireRate = w and w._stockFireRate or nil,
magazine = w and w.BulletsInMagazine or nil,
capacity = w and w.MagazineCapacity or nil,
aiming = w and w.Aiming or nil,
reloading = w and w.Reloading or nil,
}
end,
hideMenu = function() if STATE.window then STATE.window:SetOpen(false) end end,
showMenu = function() if STATE.window then STATE.window:SetOpen(true) end end,
counters = function()
local n = 0
for _ in pairs(STATE.tracks) do n = n + 1 end
local d = 0
for _ in pairs(STATE.drawings) do d = d + 1 end
return { tracked = n, drawings = d, redirected = STATE.redirected, lastError = STATE.lastError }
end,
unload = function()
RUN.alive = false
if mainConnection then mainConnection:Disconnect() mainConnection = nil end
if ProjectileController.Fire == fireHook then ProjectileController.Fire = originalFire end
if ProjectileController.FireBatch == fireBatchHook then ProjectileController.FireBatch = originalFireBatch end
if RifleClass.new ~= originalRifleNew then RifleClass.new = originalRifleNew end
for name, original in pairs(originalMethods) do
RifleClass[name] = original
end
local w = STATE.weapon
if w and w._stockFireRate ~= nil then w.FireRate = w._stockFireRate end
if w and w._stockLoadBetweenRounds ~= nil then w.LoadBetweenRounds = w._stockLoadBetweenRounds end
for model, dr in pairs(STATE.drawings or {}) do
if dr.box then dr.box:Destroy() end
if dr.label then dr.label:Destroy() end
if dr.highlight then dr.highlight:Destroy() end
end
if espGui then espGui:Destroy() espGui = nil end
getgenv().BoulderOverlay = nil
end,
}
logEvent("init", string.format("loaded | animals=%d | aim=%s",
#workspace.Animals:GetChildren(), tostring(CONFIG.aim.enabled)))
print("[boulder] loaded | animals tracked:", #workspace.Animals:GetChildren())