local function SendLengthChanged(cast: ActiveCast, lastPoint: Vector3, rayDir: Vector3, rayDisplacement: number, segmentVelocity: Vector3, cosmeticBulletObject: Instance?)
–cast.LengthChanged:Fire(cast, lastPoint, rayDir, rayDisplacement, segmentVelocity, cosmeticBulletObject)
cast.Caster.LengthChanged:Fire(cast, lastPoint, rayDir, rayDisplacement, segmentVelocity, cosmeticBulletObject)
end
– Simulate a raycast by one tick.
local function SimulateCast(cast: ActiveCast, delta: number, expectingShortCall: boolean)
assert(cast.StateInfo.UpdateConnection ~= nil, ERR_OBJECT_DISPOSED)
PrintDebug(“Casting for frame.”)
local latestTrajectory = cast.StateInfo.Trajectories[#cast.StateInfo.Trajectories]
local origin = latestTrajectory.Origin
local totalDelta = cast.StateInfo.TotalRuntime - latestTrajectory.StartTime
local initialVelocity = latestTrajectory.InitialVelocity
local acceleration = latestTrajectory.Acceleration
local lastPoint = GetPositionAtTime(totalDelta, origin, initialVelocity, acceleration)
local lastVelocity = GetVelocityAtTime(totalDelta, initialVelocity, acceleration)
local lastDelta = cast.StateInfo.TotalRuntime - latestTrajectory.StartTime
cast.StateInfo.TotalRuntime += delta
-- Recalculate this.
totalDelta = cast.StateInfo.TotalRuntime - latestTrajectory.StartTime
local currentTarget = GetPositionAtTime(totalDelta, origin, initialVelocity, acceleration)
local segmentVelocity = GetVelocityAtTime(totalDelta, initialVelocity, acceleration)
local totalDisplacement = currentTarget - lastPoint -- This is the displacement from where the ray was on the last from to where the ray is now.
local rayDir = totalDisplacement.Unit * segmentVelocity.Magnitude * delta
local targetWorldRoot = cast.RayInfo.WorldRoot
local resultOfCast = targetWorldRoot:Raycast(lastPoint, rayDir, cast.RayInfo.Parameters)
local point = currentTarget
local part: Instance? = nil
local material = Enum.Material.Air
local normal = Vector3.new()
if (resultOfCast ~= nil) then
point = resultOfCast.Position
part = resultOfCast.Instance
material = resultOfCast.Material
normal = resultOfCast.Normal
end
local rayDisplacement = (point - lastPoint).Magnitude
-- For clarity -- totalDisplacement is how far the ray would have traveled if it hit nothing,
-- and rayDisplacement is how far the ray really traveled (which will be identical to totalDisplacement if it did indeed hit nothing)
SendLengthChanged(cast, lastPoint, rayDir.Unit, rayDisplacement, segmentVelocity, cast.RayInfo.CosmeticBulletObject)
cast.StateInfo.DistanceCovered += rayDisplacement
local rayVisualization: ConeHandleAdornment? = nil
if (delta > 0) then
rayVisualization = DbgVisualizeSegment(CFrame.new(lastPoint, lastPoint + rayDir), rayDisplacement)
end
-- HIT DETECTED. Handle all that garbage, and also handle behaviors 1 and 2 (default behavior, go high res when hit) if applicable.
-- CAST BEHAVIOR 2 IS HANDLED IN THE CODE THAT CALLS THIS FUNCTION.
if part and part ~= cast.RayInfo.CosmeticBulletObject then
local start = tick()
PrintDebug("Hit something, testing now.")
-- SANITY CHECK: Don't allow the user to yield or run otherwise extensive code that takes longer than one frame/heartbeat to execute.
if (cast.RayInfo.CanPierceCallback ~= nil) then
if expectingShortCall == false then
if (cast.StateInfo.IsActivelySimulatingPierce) then
cast:Terminate()
error("ERROR: The latest call to CanPierceCallback took too long to complete! This cast is going to suffer desyncs which WILL cause unexpected behavior and errors. Please fix your performance problems, or remove statements that yield (e.g. wait() calls)")
-- Use error. This should absolutely abort the cast.
end
end
-- expectingShortCall is used to determine if we are doing a forced resolution increase, in which case this will be called several times in a single frame, which throws this error.
cast.StateInfo.IsActivelySimulatingPierce = true
end
------------------------------
if cast.RayInfo.CanPierceCallback == nil or (cast.RayInfo.CanPierceCallback ~= nil and cast.RayInfo.CanPierceCallback(cast, resultOfCast, segmentVelocity, cast.RayInfo.CosmeticBulletObject) == false) then
PrintDebug("Piercing function is nil or it returned FALSE to not pierce this hit.")
cast.StateInfo.IsActivelySimulatingPierce = false
if (cast.StateInfo.HighFidelityBehavior == 2 and latestTrajectory.Acceleration ~= Vector3.new() and cast.StateInfo.HighFidelitySegmentSize ~= 0) then
cast.StateInfo.CancelHighResCast = false -- Reset this here.
if cast.StateInfo.IsActivelyResimulating then
cast:Terminate()
error("Cascading cast lag encountered! The caster attempted to perform a high fidelity cast before the previous one completed, resulting in exponential cast lag. Consider increasing HighFidelitySegmentSize.")
end
cast.StateInfo.IsActivelyResimulating = true
-- This is a physics based cast and it needs to be recalculated.
PrintDebug("Hit was registered, but recalculation is on for physics based casts. Recalculating to verify a real hit...")
-- Split this ray segment into smaller segments of a given size.
-- In 99% of cases, it won't divide evently (e.g. I have a distance of 1.25 and I want to divide into 0.1 -- that won't work)
-- To fix this, the segments need to be stretched slightly to fill the space (rather than having a single shorter segment at the end)
local numSegmentsDecimal = rayDisplacement / cast.StateInfo.HighFidelitySegmentSize -- say rayDisplacement is 5.1, segment size is 0.5 -- 10.2 segments
local numSegmentsReal = math.floor(numSegmentsDecimal) -- 10 segments + 0.2 extra segments
local realSegmentLength = rayDisplacement / numSegmentsReal -- this spits out 0.51, which isn't exact to the defined 0.5, but it's close
-- Now the real hard part is converting this to time.
local timeIncrement = delta / numSegmentsReal
for segmentIndex = 1, numSegmentsReal do
if cast.StateInfo.CancelHighResCast then
cast.StateInfo.CancelHighResCast = false
break
end
local subPosition = GetPositionAtTime(lastDelta + (timeIncrement * segmentIndex), origin, initialVelocity, acceleration)
local subVelocity = GetVelocityAtTime(lastDelta + (timeIncrement * segmentIndex), initialVelocity, acceleration)
local subRayDir = subVelocity * delta
local subResult = targetWorldRoot:Raycast(subPosition, subRayDir, cast.RayInfo.Parameters)
local subDisplacement = (subPosition - (subPosition + subVelocity)).Magnitude
if (subResult ~= nil) then
local subDisplacement = (subPosition - subResult.Position).Magnitude
local dbgSeg = DbgVisualizeSegment(CFrame.new(subPosition, subPosition + subVelocity), subDisplacement)
if (dbgSeg ~= nil) then dbgSeg.Color3 = Color3.new(0.286275, 0.329412, 0.247059) end
if cast.RayInfo.CanPierceCallback == nil or (cast.RayInfo.CanPierceCallback ~= nil and cast.RayInfo.CanPierceCallback(cast, subResult, subVelocity, cast.RayInfo.CosmeticBulletObject) == false) then
-- Still hit even at high res
cast.StateInfo.IsActivelyResimulating = false
SendRayHit(cast, subResult, subVelocity, cast.RayInfo.CosmeticBulletObject)
cast:Terminate()
local vis = DbgVisualizeHit(CFrame.new(point), false)
if (vis ~= nil) then vis.Color3 = Color3.new(0.0588235, 0.87451, 1) end
return
else
-- Recalculating hit something pierceable instead.
SendRayPierced(cast, subResult, subVelocity, cast.RayInfo.CosmeticBulletObject) -- This may result in CancelHighResCast being set to true.
local vis = DbgVisualizeHit(CFrame.new(point), true)
if (vis ~= nil) then vis.Color3 = Color3.new(1, 0.113725, 0.588235) end
if (dbgSeg ~= nil) then dbgSeg.Color3 = Color3.new(0.305882, 0.243137, 0.329412) end
end
else
local dbgSeg = DbgVisualizeSegment(CFrame.new(subPosition, subPosition + subVelocity), subDisplacement)
if (dbgSeg ~= nil) then dbgSeg.Color3 = Color3.new(0.286275, 0.329412, 0.247059) end
end
end
-- If the script makes it here, then it wasn't a real hit (higher resolution revealed that the low-res hit was faulty)
-- Just let it keep going.
cast.StateInfo.IsActivelyResimulating = false
elseif (cast.StateInfo.HighFidelityBehavior ~= 1 and cast.StateInfo.HighFidelityBehavior ~= 3) then
cast:Terminate()
error("Invalid value " .. (cast.StateInfo.HighFidelityBehavior) .. " for HighFidelityBehavior.")
else
-- This is not a physics cast, or recalculation is off.
PrintDebug("Hit was successful. Terminating.")
SendRayHit(cast, resultOfCast, segmentVelocity, cast.RayInfo.CosmeticBulletObject)
cast:Terminate()
DbgVisualizeHit(CFrame.new(point), false)
return
end
else
PrintDebug("Piercing function returned TRUE to pierce this part.")
if rayVisualization ~= nil then
rayVisualization.Color3 = Color3.new(0.4, 0.05, 0.05) -- Turn it red to signify that the cast was scrapped.
end
DbgVisualizeHit(CFrame.new(point), true)
local params = cast.RayInfo.Parameters
local alteredParts = {}
local currentPierceTestCount = 0
local originalFilter = params.FilterDescendantsInstances
local brokeFromSolidObject = false
while true do
-- So now what I need to do is redo this entire cast, just with the new filter list
-- Catch case: Is it terrain?
if resultOfCast.Instance:IsA("Terrain") then
if material == Enum.Material.Water then
-- Special case: Pierced on water?
cast:Terminate()
error("Do not add Water as a piercable material. If you need to pierce water, set cast.RayInfo.Parameters.IgnoreWater = true instead", 0)
end
warn("WARNING: The pierce callback for this cast returned TRUE on Terrain! This can cause severely adverse effects.")
end
if params.FilterType == Enum.RaycastFilterType.Blacklist then
-- blacklist
-- DO NOT DIRECTLY TABLE.INSERT ON THE PROPERTY
local filter = params.FilterDescendantsInstances
table.insert(filter, resultOfCast.Instance)
table.insert(alteredParts, resultOfCast.Instance)
params.FilterDescendantsInstances = filter
else
-- whitelist
-- method implemeneted by custom table system
-- DO NOT DIRECTLY TABLE.REMOVEOBJECT ON THE PROPERTY
local filter = params.FilterDescendantsInstances
table.removeObject(filter, resultOfCast.Instance)
table.insert(alteredParts, resultOfCast.Instance)
params.FilterDescendantsInstances = filter
end
SendRayPierced(cast, resultOfCast, segmentVelocity, cast.RayInfo.CosmeticBulletObject)
-- List has been updated, so let's cast again.
resultOfCast = targetWorldRoot:Raycast(lastPoint, rayDir, params)
-- No hit? No simulation. Break.
if resultOfCast == nil then
break
end
if currentPierceTestCount >= MAX_PIERCE_TEST_COUNT then
warn("WARNING: Exceeded maximum pierce test budget for a single ray segment (attempted to test the same segment " .. MAX_PIERCE_TEST_COUNT .. " times!)")
break
end
currentPierceTestCount = currentPierceTestCount + 1;
if cast.RayInfo.CanPierceCallback(cast, resultOfCast, segmentVelocity, cast.RayInfo.CosmeticBulletObject) == false then
brokeFromSolidObject = true
break
end
end
-- Restore the filter to its default state.
cast.RayInfo.Parameters.FilterDescendantsInstances = originalFilter
cast.StateInfo.IsActivelySimulatingPierce = false
if brokeFromSolidObject then
-- We actually hit something while testing.
PrintDebug("Broke because the ray hit something solid (" .. tostring(resultOfCast.Instance) .. ") while testing for a pierce. Terminating the cast.")
SendRayHit(cast, resultOfCast, segmentVelocity, cast.RayInfo.CosmeticBulletObject)
cast:Terminate()
DbgVisualizeHit(CFrame.new(resultOfCast.Position), false)
return
end
-- And exit the function here too.
end
end
if (cast.StateInfo.DistanceCovered >= cast.RayInfo.MaxDistance) then
-- SendRayHit(cast, nil, segmentVelocity, cast.RayInfo.CosmeticBulletObject)
cast:Terminate()
DbgVisualizeHit(CFrame.new(currentTarget), false)
end
end
i found this inside FastCastRedux > ActiveCast