Last active
June 24, 2024 15:47
-
-
Save cozywitchcraft/00c90382ff2b157d23ad246819e32e6b to your computer and use it in GitHub Desktop.
Old version of https://github.com/cozywitchcraft/anti-wallhop/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- StarterCharacterScripts/AntiWallhop | |
local RunService = game:GetService("RunService") | |
local CAST_POINTS = { | |
-- Center line | |
Vector3.new(0, -0.75, 0), | |
Vector3.new(0, -0.75, 1), | |
Vector3.new(0, -0.75, -1), | |
-- Corners | |
Vector3.new(-1, -0.75, -1), | |
Vector3.new(-1, -0.75, 1), | |
Vector3.new(1, -0.75, -1), | |
Vector3.new(1, -0.75, 1), | |
} | |
local DebugVisuals = require(script.DebugVisuals) | |
local character = script.Parent | |
local humanoid = character:WaitForChild("Humanoid") :: Humanoid | |
local rootPart = character:WaitForChild("HumanoidRootPart") :: BasePart | |
-- Saves an __index call per frame | |
local isRigTypeR6 = humanoid.RigType == Enum.HumanoidRigType.R6 | |
local params = RaycastParams.new() | |
params.CollisionGroup = rootPart.CollisionGroup | |
params.FilterType = Enum.RaycastFilterType.Exclude | |
params.FilterDescendantsInstances = { character } | |
params.RespectCanCollide = true | |
params.IgnoreWater = true | |
local postSimulationConnection | |
local function getHipHeight(halfSize: Vector3): number | |
-- Using halfSize saves an __index call | |
local hipHeight = 0.6 * halfSize.Y + humanoid.HipHeight | |
-- R15 only relies on the hip height while R6 takes legs into consideration | |
if isRigTypeR6 then | |
local legHeight = 0 | |
local leftLeg = character:FindFirstChild("Left Leg") | |
local rightLeg = character:FindFirstChild("Right Leg") | |
if leftLeg then | |
legHeight = math.max(legHeight, leftLeg.Size.Y) | |
end | |
if rightLeg then | |
legHeight = math.max(legHeight, rightLeg.Size.Y) | |
end | |
hipHeight += legHeight | |
end | |
return hipHeight | |
end | |
local function isGrounded(): boolean | |
local cframe = rootPart.CFrame | |
local halfSize = 0.4 * rootPart.Size -- 0.1-stud skin | |
local factor = 1.5 | |
local verticalVelocity = rootPart.AssemblyLinearVelocity.Y | |
local minVelocity = -125 * halfSize.Y | |
if verticalVelocity > 0 then | |
return false -- Prevents double jumping | |
elseif verticalVelocity < minVelocity then | |
factor += verticalVelocity / minVelocity -- Prevents bouncing | |
end | |
local direction = getHipHeight(halfSize) * factor * -Vector3.yAxis | |
DebugVisuals.reset() | |
for _, point in CAST_POINTS do | |
local origin = cframe:PointToWorldSpace(halfSize * point) | |
local result = workspace:Raycast(origin, direction, params) | |
DebugVisuals.drawLine(origin, direction, result) | |
if result then | |
return true | |
end | |
end | |
return false | |
end | |
local function onPostSimulation(deltaTime: number) | |
debug.profilebegin("AntiWallhop::onPostSimulation") | |
local isFalling = humanoid:GetState() == Enum.HumanoidStateType.Freefall | |
-- We're only worried about detecting the floor while the humanoid is falling. | |
-- This is because wallhopping only occurs when entering the landing state pre- | |
-- simulation, so we move it post-simulation. | |
if isFalling and isGrounded() then | |
humanoid:ChangeState(Enum.HumanoidStateType.Landed) | |
end | |
debug.profileend() | |
end | |
local function enableAntiWallhop() | |
if postSimulationConnection then | |
return | |
end | |
-- Disable humanoid's built-in floor detection | |
humanoid:SetStateEnabled(Enum.HumanoidStateType.Landed, false) | |
-- Start custom floor detection after physics simulation | |
postSimulationConnection = RunService.PostSimulation:Connect(onPostSimulation) | |
end | |
local function disableAntiWallhop() | |
if not postSimulationConnection then | |
return | |
end | |
humanoid:SetStateEnabled(Enum.HumanoidStateType.Landed, true) | |
postSimulationConnection = postSimulationConnection:Disconnect() | |
end | |
local function toggleAntiWallhop() | |
if script:GetAttribute("AntiWallhopEnabled") then | |
enableAntiWallhop() | |
else | |
disableAntiWallhop() | |
end | |
end | |
local function toggleDebugVisuals() | |
if script:GetAttribute("ShowDebugVisuals") then | |
DebugVisuals.mount(character) | |
else | |
DebugVisuals.unmount() | |
end | |
end | |
-- Support collision groups (e.g. player-to-player non-collision) | |
rootPart:GetPropertyChangedSignal("CollisionGroup"):Connect(function() | |
params.CollisionGroup = rootPart.CollisionGroup | |
end) | |
script:GetAttributeChangedSignal("AntiWallhopEnabled"):Connect(toggleAntiWallhop) | |
script:GetAttributeChangedSignal("ShowDebugVisuals"):Connect(toggleDebugVisuals) | |
toggleAntiWallhop() | |
toggleDebugVisuals() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local MAX_LINES = 7 | |
local isShowing = false | |
local lines: { LineHandleAdornment } = {} | |
local lineIndex = 1 | |
local terrain = workspace.Terrain | |
local DebugVisuals = {} | |
function DebugVisuals.mount(character: Model) | |
if isShowing then | |
return | |
end | |
for _ = 1, MAX_LINES do | |
local line = Instance.new("LineHandleAdornment") | |
line.Visible = false | |
line.Adornee = terrain | |
-- HACK: Lines are destroyed when character is destroyed | |
line.Parent = character | |
table.insert(lines, line) | |
end | |
isShowing = true | |
end | |
function DebugVisuals.unmount() | |
if not isShowing then | |
return | |
end | |
for _, line in lines do | |
line:Destroy() | |
end | |
table.clear(lines) | |
isShowing = false | |
end | |
function DebugVisuals.drawLine(origin, direction, result) | |
if not isShowing then | |
return | |
end | |
local color = Color3.new(result and 0 or 1, result and 1 or 0, 0) | |
local line = lines[lineIndex] | |
line.CFrame = CFrame.new(origin, origin + direction) | |
line.Length = direction.Magnitude | |
line.Thickness = 3 | |
line.Color3 = color | |
line.Visible = true | |
lineIndex += 1 | |
end | |
function DebugVisuals.reset() | |
if not isShowing then | |
return | |
end | |
for _, line in lines do | |
line.Visible = false | |
end | |
lineIndex = 1 | |
end | |
return DebugVisuals |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment