mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
Upload
This commit is contained in:
843
lua/autorun/smartsnap.lua
Normal file
843
lua/autorun/smartsnap.lua
Normal file
@@ -0,0 +1,843 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
|
||||
|
||||
Written by Syranide, me@syranide.com
|
||||
fixed and updated by minifisch, mail@minifisch.net
|
||||
big thanks to Syranide! :)
|
||||
|
||||
|
||||
]]
|
||||
--
|
||||
if SERVER then
|
||||
AddCSLuaFile()
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local target = {
|
||||
active = false
|
||||
}
|
||||
|
||||
local snaptarget = {
|
||||
active = false
|
||||
}
|
||||
|
||||
local snapkey = false
|
||||
local snaptime = false
|
||||
local snaplock = false
|
||||
local snapclick = false
|
||||
local snapclickfade = 0
|
||||
local snapcursor = false
|
||||
local snapspawnmenu = false
|
||||
|
||||
local cache = {
|
||||
vPlayerPos = 0,
|
||||
vLookPos = 0,
|
||||
vLookClipPos = 0,
|
||||
vLookVector = 0
|
||||
}
|
||||
|
||||
local condefs = {
|
||||
snap_enabled = 1,
|
||||
snap_gcboost = 1,
|
||||
snap_gcstrength = 125,
|
||||
snap_hidegrid = 0,
|
||||
snap_clickgrid = 0,
|
||||
snap_toggledelay = 0,
|
||||
snap_disableuse = 0,
|
||||
snap_allentities = 0,
|
||||
snap_alltools = 0,
|
||||
snap_enabletoggle = 0,
|
||||
snap_lockdelay = 0.5,
|
||||
snap_distance = 250,
|
||||
snap_gridlimit = 16,
|
||||
snap_gridsize = 8,
|
||||
snap_gridalpha = 0.4,
|
||||
snap_gridoffset = 0.5,
|
||||
snap_boundingbox = 1,
|
||||
snap_revertaim = 1,
|
||||
snap_centerline = 1
|
||||
}
|
||||
|
||||
local convars = {}
|
||||
|
||||
for key, value in pairs(condefs) do
|
||||
convars[#convars + 1] = key
|
||||
end
|
||||
|
||||
local modelsaveset = {}
|
||||
local modeloffsets = {}
|
||||
|
||||
local function DrawScreenLine(vsA, vsB)
|
||||
surface.DrawLine(vsA.x, vsA.y, vsB.x, vsB.y)
|
||||
end
|
||||
|
||||
local function ToScreen(vWorld)
|
||||
local vsScreen = vWorld:ToScreen()
|
||||
|
||||
return Vector(vsScreen.x, vsScreen.y, 0)
|
||||
end
|
||||
|
||||
local function PointToScreen(vPoint)
|
||||
if cache.vLookVector:DotProduct(vPoint - cache.vLookClipPos) > 0 then return ToScreen(vPoint) end
|
||||
end
|
||||
|
||||
local function LineToScreen(vStart, vEnd)
|
||||
local dotStart = cache.vLookVector:DotProduct(vStart - cache.vLookClipPos)
|
||||
local dotEnd = cache.vLookVector:DotProduct(vEnd - cache.vLookClipPos)
|
||||
|
||||
if dotStart > 0 and dotEnd > 0 then
|
||||
return ToScreen(vStart), ToScreen(vEnd)
|
||||
elseif dotStart > 0 or dotEnd > 0 then
|
||||
local vLength = vEnd - vStart
|
||||
local vIntersect = vStart + vLength * ((cache.vLookClipPos:DotProduct(cache.vLookVector) - vStart:DotProduct(cache.vLookVector)) / vLength:DotProduct(cache.vLookVector))
|
||||
|
||||
if dotStart <= 0 then
|
||||
return ToScreen(vIntersect), ToScreen(vEnd)
|
||||
else
|
||||
return ToScreen(vStart), ToScreen(vIntersect)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RayQuadIntersect(vOrigin, vDirection, vPlane, vX, vY)
|
||||
local vp = vDirection:Cross(vY)
|
||||
local d = vX:DotProduct(vp)
|
||||
if (d <= 0.0) then return end
|
||||
local vt = vOrigin - vPlane
|
||||
local u = vt:DotProduct(vp)
|
||||
if (u < 0.0 or u > d) then return end
|
||||
local v = vDirection:DotProduct(vt:Cross(vX))
|
||||
if (v < 0.0 or v > d) then return end
|
||||
|
||||
return Vector(u / d, v / d, 0)
|
||||
end
|
||||
|
||||
local function OnInitialize()
|
||||
for key, value in pairs(condefs) do
|
||||
CreateClientConVar(key, value, true, false)
|
||||
end
|
||||
|
||||
for _, filename in ipairs(file.Find('smartsnap_offsets_*.png', "GAME")) do
|
||||
local file = file.Read(filename)
|
||||
|
||||
if file then
|
||||
lines = string.Explode("\n", file)
|
||||
header = table.remove(lines, 1)
|
||||
|
||||
if header == "SMARTSNAP_OFFSETS" then
|
||||
for _, line in ipairs(lines) do
|
||||
local pos = string.find(line, '=')
|
||||
|
||||
if pos then
|
||||
local key = string.lower(string.Trim(string.sub(line, 1, pos - 1)))
|
||||
local value = string.Trim(string.sub(line, pos + 1))
|
||||
local c = string.Explode(",", value)
|
||||
modeloffsets[key] = {tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]), tonumber(c[5]), tonumber(c[6])}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnShutDown()
|
||||
output = file.Read('smartsnap_offsets_custom.png')
|
||||
|
||||
if output == nil then
|
||||
output = "SMARTSNAP_OFFSETS\n"
|
||||
end
|
||||
|
||||
for model, _ in pairs(modelsaveset) do
|
||||
output = output .. model .. '=' .. table.concat(modeloffsets[model], ",") .. "\n"
|
||||
end
|
||||
|
||||
file.Write('smartsnap_offsets_custom.png', output)
|
||||
end
|
||||
|
||||
local function GetDevOffset()
|
||||
local model = string.lower(target.entity:GetModel())
|
||||
|
||||
if modeloffsets[model] == nil then
|
||||
modeloffsets[model] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
|
||||
end
|
||||
|
||||
return modeloffsets[model]
|
||||
end
|
||||
|
||||
concommand.Add("snap_dev_alloffset", function(player, command, arguments)
|
||||
if target.active == true then
|
||||
if #arguments >= 1 then
|
||||
local v = GetDevOffset()
|
||||
|
||||
for i = 1, 6 do
|
||||
v[i] = v[i] + tonumber(arguments[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("snap_dev_gridoffset", function(player, command, arguments)
|
||||
if target.active == true then
|
||||
if #arguments >= 1 then
|
||||
local v = GetDevOffset()
|
||||
v[target.face] = v[target.face] + tonumber(arguments[1])
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("snap_dev_saveoffset", function(player, command, arguments)
|
||||
if target.active == true then
|
||||
local v = GetDevOffset()
|
||||
modelsaveset[string.lower(target.entity:GetModel())] = true
|
||||
end
|
||||
end)
|
||||
|
||||
local function SnapToggleGrid()
|
||||
if (GetConVarNumber("snap_enabled") == 0) then
|
||||
RunConsoleCommand('snap_enabled', '1')
|
||||
else
|
||||
RunConsoleCommand('snap_enabled', '0')
|
||||
end
|
||||
end
|
||||
|
||||
local function SnapPress()
|
||||
if GetConVarNumber("snap_clickgrid") ~= 0 and not snapclick then
|
||||
snapclick = true
|
||||
snapclickfade = CurTime()
|
||||
elseif GetConVarNumber("snap_clickgrid") == 0 or snapclick then
|
||||
if (snaplock or snapcursor) then
|
||||
snaptime = false
|
||||
else
|
||||
local toggledelay = GetConVarNumber("snap_toggledelay")
|
||||
|
||||
if (toggledelay > 0 and snaptime and snaptime + toggledelay > CurTime()) then
|
||||
SnapToggleGrid()
|
||||
snaptime = false
|
||||
snaplock = false
|
||||
else
|
||||
snaptime = CurTime()
|
||||
end
|
||||
end
|
||||
|
||||
snapkey = target.active
|
||||
|
||||
if (not snapcursor) then
|
||||
snaplock = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function SnapRelease()
|
||||
snapkey = false
|
||||
end
|
||||
|
||||
local function SnapLock()
|
||||
snaplock = not snaplock
|
||||
end
|
||||
|
||||
local function OnSpawnMenu()
|
||||
snapspawnmenu = true
|
||||
end
|
||||
|
||||
local function OnKeyPress(player, key)
|
||||
if (key == IN_USE and GetConVarNumber("snap_disableuse") == 0) then
|
||||
SnapPress()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnKeyRelease(player, key)
|
||||
if (key == IN_USE and GetConVarNumber("snap_disableuse") == 0) then
|
||||
SnapRelease()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnThink()
|
||||
if (vgui.CursorVisible()) then
|
||||
if (not snapcursor and snaplock) then
|
||||
snaptarget = table.Copy(target)
|
||||
end
|
||||
|
||||
snaptime = false
|
||||
snapcursor = true
|
||||
else
|
||||
if (snapcursor and snaplock) then
|
||||
target = snaptarget
|
||||
end
|
||||
|
||||
snapspawnmenu = false
|
||||
snapcursor = false
|
||||
end
|
||||
|
||||
if (GetConVarNumber("snap_enabletoggle") ~= 0) then
|
||||
if (snapkey and snaptime and not snaplock) then
|
||||
if (CurTime() > snaptime + GetConVarNumber("snap_lockdelay")) then
|
||||
snaplock = true
|
||||
snaptime = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local locked = target.locked and target.active
|
||||
target.locked = (snapkey or snaplock and not snapcursor) and target.active
|
||||
|
||||
if (not target.locked and locked and GetConVarNumber("snap_revertaim") ~= 0) then
|
||||
if (snapcursor) then
|
||||
local screen = target.entity:LocalToWorld(target.vector):ToScreen()
|
||||
gui.SetMousePos(math.Round(screen.x), math.Round(screen.y))
|
||||
else
|
||||
local angles = (target.entity:LocalToWorld(target.vector) - LocalPlayer():GetShootPos()):Angle()
|
||||
LocalPlayer():SetEyeAngles(angles)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function CalculateGridAxis(L)
|
||||
local length = L:Length()
|
||||
local grid = math.Clamp(math.floor(length / (2 * GetConVarNumber("snap_gridsize"))) * 2, 2, GetConVarNumber("snap_gridlimit"))
|
||||
local offset = math.Clamp(GetConVarNumber("snap_gridoffset") / length, 0, 1 / grid)
|
||||
local scale = 1 - offset * 2
|
||||
|
||||
return {
|
||||
length = length,
|
||||
offset = offset,
|
||||
scale = scale,
|
||||
grid = grid
|
||||
}
|
||||
end
|
||||
|
||||
local function CalculateSnap(X, Y, v)
|
||||
local LX = CalculateGridAxis(X)
|
||||
local LY = CalculateGridAxis(Y)
|
||||
local BX = math.Clamp(math.Round(v.x * LX.grid), 0, LX.grid)
|
||||
local BY = math.Clamp(math.Round(v.y * LY.grid), 0, LY.grid)
|
||||
|
||||
if BX == 1 and v.x < (1 / LX.grid + LX.offset) / 2 then
|
||||
BX = 0
|
||||
end
|
||||
|
||||
if BX == LX.grid - 1 and v.x > 1 - (1 / LX.grid + LX.offset) / 2 then
|
||||
BX = LX.grid
|
||||
end
|
||||
|
||||
if BY == 1 and v.y < (1 / LY.grid + LY.offset) / 2 then
|
||||
BY = 0
|
||||
end
|
||||
|
||||
if BY == LY.grid - 1 and v.y > 1 - (1 / LY.grid + LY.offset) / 2 then
|
||||
BY = LY.grid
|
||||
end
|
||||
|
||||
local RX = X * (BX / LX.grid)
|
||||
local RY = Y * (BY / LY.grid)
|
||||
|
||||
if BX == 0 then
|
||||
RX = X * math.Clamp(LX.offset, 0, 1 / LX.grid)
|
||||
end
|
||||
|
||||
if BX == LX.grid then
|
||||
RX = X * (1 - math.Clamp(LX.offset, 0, 1 / LX.grid))
|
||||
end
|
||||
|
||||
if BY == 0 then
|
||||
RY = Y * math.Clamp(LY.offset, 0, 1 / LY.grid)
|
||||
end
|
||||
|
||||
if BY == LY.grid then
|
||||
RY = Y * (1 - math.Clamp(LY.offset, 0, 1 / LY.grid))
|
||||
end
|
||||
|
||||
return RX + RY
|
||||
end
|
||||
|
||||
local function DrawGridLines(vOrigin, vSX, vSY, gridLines, offsetX, offsetY, sign)
|
||||
local centerline = (GetConVarNumber("snap_centerline") ~= 0)
|
||||
local vTemp = vOrigin + vSX * 0.5
|
||||
local vX = vTemp + vSY * (offsetY)
|
||||
local vY = vTemp + vSY * (1 - offsetY)
|
||||
local vOffset, temp
|
||||
local xtemp = ToScreen(vX) - ToScreen(vY)
|
||||
xtemp:Normalize()
|
||||
local vsNormal = xtemp
|
||||
|
||||
if math.abs(vsNormal.x) < 1 - math.abs(vsNormal.y) then
|
||||
temp = -0.5 * sign
|
||||
else
|
||||
temp = 0.5 * sign
|
||||
end
|
||||
|
||||
if math.abs(vsNormal.x) < math.abs(vsNormal.y) then
|
||||
vsOffset = Vector(temp, 0, 0)
|
||||
else
|
||||
vsOffset = Vector(0, temp, 0)
|
||||
end
|
||||
|
||||
if offsetX < 1 / gridLines then
|
||||
local vTemp = vOrigin + vSX * offsetX
|
||||
local vX = vTemp + vSY * offsetY
|
||||
local vY = vTemp + vSY * (1 - offsetY)
|
||||
local vsX, vsY = LineToScreen(vX, vY)
|
||||
|
||||
if (vsX) then
|
||||
DrawScreenLine(vsX + vsOffset, vsY + vsOffset)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, gridLines - 1 do
|
||||
local vTemp = vOrigin + vSX * (i / gridLines)
|
||||
local vX = vTemp + vSY * offsetY
|
||||
local vY = vTemp + vSY * (1 - offsetY)
|
||||
local vsX, vsY = LineToScreen(vX, vY)
|
||||
|
||||
if (vsX) then
|
||||
if (gridLines / i == 2 and centerline) then
|
||||
DrawScreenLine(vsX + vsOffset * -1, vsY + vsOffset * -1)
|
||||
DrawScreenLine(vsX + vsOffset * 3, vsY + vsOffset * 3)
|
||||
else
|
||||
DrawScreenLine(vsX + vsOffset, vsY + vsOffset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if offsetX < 1 / gridLines then
|
||||
local vTemp = vOrigin + vSX * (1 - offsetX)
|
||||
local vX = vTemp + vSY * offsetY
|
||||
local vY = vTemp + vSY * (1 - offsetY)
|
||||
local vsX, vsY = LineToScreen(vX, vY)
|
||||
|
||||
if (vsX) then
|
||||
DrawScreenLine(vsX + vsOffset, vsY + vsOffset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawGrid(vOrigin, vSX, vSY)
|
||||
local LX = CalculateGridAxis(vSX)
|
||||
local LY = CalculateGridAxis(vSY)
|
||||
surface.SetDrawColor(0, 0, 0, math.Round(GetConVarNumber("snap_gridalpha") * 255))
|
||||
DrawGridLines(vOrigin, vSX, vSY, LX.grid, LX.offset, LY.offset, 1)
|
||||
DrawGridLines(vOrigin, vSY, vSX, LY.grid, LY.offset, LX.offset, 1)
|
||||
surface.SetDrawColor(255, 255, 255, math.Round(GetConVarNumber("snap_gridalpha") * 255))
|
||||
DrawGridLines(vOrigin, vSX, vSY, LX.grid, LX.offset, LY.offset, -1)
|
||||
DrawGridLines(vOrigin, vSY, vSX, LY.grid, LY.offset, LX.offset, -1)
|
||||
end
|
||||
|
||||
local function DrawBoundaryLines(vOrigin, vOpposite)
|
||||
local vPoint
|
||||
|
||||
if (vOrigin:Distance(vOpposite) > 5) then
|
||||
local x = vOpposite - vOrigin
|
||||
x:Normalize()
|
||||
vPoint = vOrigin + x * 5
|
||||
else
|
||||
vPoint = vOrigin + (vOpposite - vOrigin) / 2
|
||||
end
|
||||
|
||||
local vsA, vsB = LineToScreen(vPoint, vOrigin)
|
||||
|
||||
if (vsA) then
|
||||
surface.SetDrawColor(0, 0, 255, 192)
|
||||
DrawScreenLine(vsA, vsB)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawBoundary(vOrigin, vX, vY, vZ)
|
||||
DrawBoundaryLines(vOrigin, vX)
|
||||
DrawBoundaryLines(vOrigin, vY)
|
||||
DrawBoundaryLines(vOrigin, vZ)
|
||||
end
|
||||
|
||||
local function DrawSnapCross(vsCenter, r, g, b)
|
||||
surface.SetDrawColor(0, 0, 0, 255)
|
||||
DrawScreenLine(vsCenter + Vector(-2.5, -2.0), vsCenter + Vector(2.5, 3.0))
|
||||
DrawScreenLine(vsCenter + Vector(1.5, -2.0), vsCenter + Vector(-3.5, 3.0))
|
||||
surface.SetDrawColor(r, g, b, 255)
|
||||
DrawScreenLine(vsCenter + Vector(-1.5, -2.0), vsCenter + Vector(3.5, 3.0))
|
||||
DrawScreenLine(vsCenter + Vector(2.5, -2.0), vsCenter + Vector(-2.5, 3.0))
|
||||
end
|
||||
|
||||
local function ComputeEdges(entity, obbmax, obbmin)
|
||||
return {
|
||||
lsw = entity:LocalToWorld(Vector(obbmin.x, obbmin.y, obbmin.z)),
|
||||
lse = entity:LocalToWorld(Vector(obbmax.x, obbmin.y, obbmin.z)),
|
||||
lnw = entity:LocalToWorld(Vector(obbmin.x, obbmax.y, obbmin.z)),
|
||||
lne = entity:LocalToWorld(Vector(obbmax.x, obbmax.y, obbmin.z)),
|
||||
usw = entity:LocalToWorld(Vector(obbmin.x, obbmin.y, obbmax.z)),
|
||||
use = entity:LocalToWorld(Vector(obbmax.x, obbmin.y, obbmax.z)),
|
||||
unw = entity:LocalToWorld(Vector(obbmin.x, obbmax.y, obbmax.z)),
|
||||
une = entity:LocalToWorld(Vector(obbmax.x, obbmax.y, obbmax.z))
|
||||
}
|
||||
end
|
||||
|
||||
local function OnPaintHUD()
|
||||
target.active = false
|
||||
if GetConVarNumber("snap_clickgrid") ~= 0 and not snapclick then return end
|
||||
snapclickprev = snapclick
|
||||
snapclick = snapclickprev and snapclickfade > CurTime()
|
||||
if (GetConVarNumber("snap_enabled") == 0) then return end
|
||||
if (not LocalPlayer():Alive() or LocalPlayer():InVehicle()) then return end
|
||||
|
||||
if (target.locked) then
|
||||
if (not target.entity:IsValid()) then return end
|
||||
else
|
||||
local trace = LocalPlayer():GetEyeTrace()
|
||||
cache.vLookTrace = trace
|
||||
if (not trace.HitNonWorld) then return end
|
||||
local entity = trace.Entity
|
||||
if (entity == nil) then return end
|
||||
if (not entity:IsValid()) then return end
|
||||
local class = entity:GetClass()
|
||||
if (class ~= 'prop_physics' and class ~= 'phys_magnet' and class ~= 'gmod_spawner' and GetConVarNumber('snap_allentities') == 0 or class == 'player') then return end
|
||||
if (not LocalPlayer():GetActiveWeapon():IsValid()) then return end
|
||||
if (LocalPlayer():GetActiveWeapon():GetClass() == 'weapon_physgun') then return end
|
||||
if (LocalPlayer():GetActiveWeapon():GetClass() ~= 'gmod_tool' and GetConVarNumber('snap_alltools') == 0) then return end
|
||||
target.entity = entity
|
||||
end
|
||||
|
||||
--ErrorNoHalt(collectgarbage("count"))
|
||||
if GetConVarNumber("snap_gcboost") ~= 0 then
|
||||
collectgarbage("step", GetConVarNumber("snap_gcstrength"))
|
||||
end
|
||||
|
||||
snapclick = snapclickprev
|
||||
snapclickfade = CurTime() + 0.25
|
||||
-- updating the cache perhaps shouldn't be done here, CalcView?
|
||||
cache.vLookPos = LocalPlayer():GetShootPos()
|
||||
cache.vLookVector = LocalPlayer():GetAimVector()
|
||||
cache.vLookClipPos = cache.vLookPos + cache.vLookVector * 3
|
||||
local model = string.lower(target.entity:GetModel())
|
||||
local offsets = modeloffsets[model]
|
||||
|
||||
if not offsets then
|
||||
local offset = 0.25
|
||||
offsets = {offset, offset, offset, offset, offset, offset}
|
||||
end
|
||||
|
||||
if cache.eEntity ~= target.entity or cache.vEntAngles ~= target.entity:GetAngles() or vEntPosition ~= target.entity:GetPos() then
|
||||
cache.eEntity = target.entity
|
||||
cache.vEntAngles = target.entity:GetAngles()
|
||||
cache.vEntPosition = target.entity:GetPos()
|
||||
local obbmax = target.entity:OBBMaxs()
|
||||
local obbmin = target.entity:OBBMins()
|
||||
local obvsnap = ComputeEdges(target.entity, obbmax, obbmin)
|
||||
local obbmax = target.entity:OBBMaxs() - Vector(offsets[5], offsets[3], offsets[1])
|
||||
local obbmin = target.entity:OBBMins() + Vector(offsets[6], offsets[4], offsets[2])
|
||||
local obvgrid = ComputeEdges(target.entity, obbmax, obbmin)
|
||||
local faces = {{obvgrid.unw, obvgrid.usw - obvgrid.unw, obvgrid.une - obvgrid.unw, obvgrid.lnw - obvgrid.unw, Vector(0, 0, -offsets[1])}, {obvgrid.lsw, obvgrid.lnw - obvgrid.lsw, obvgrid.lse - obvgrid.lsw, obvgrid.usw - obvgrid.lsw, Vector(0, 0, offsets[2])}, {obvgrid.unw, obvgrid.une - obvgrid.unw, obvgrid.lnw - obvgrid.unw, obvgrid.usw - obvgrid.unw, Vector(0, -offsets[3], 0)}, {obvgrid.usw, obvgrid.lsw - obvgrid.usw, obvgrid.use - obvgrid.usw, obvgrid.unw - obvgrid.usw, Vector(0, offsets[4], 0)}, {obvgrid.une, obvgrid.use - obvgrid.une, obvgrid.lne - obvgrid.une, obvgrid.unw - obvgrid.une, Vector(-offsets[5], 0, 0)}, {obvgrid.unw, obvgrid.lnw - obvgrid.unw, obvgrid.usw - obvgrid.unw, obvgrid.une - obvgrid.unw, Vector(offsets[6], 0, 0)}}
|
||||
cache.aGrid = obvgrid
|
||||
cache.aSnap = obvsnap
|
||||
cache.aFaces = faces
|
||||
end
|
||||
|
||||
local obvgrid = cache.aGrid
|
||||
local obvsnap = cache.aSnap
|
||||
local faces = cache.aFaces
|
||||
|
||||
if (not target.locked) then
|
||||
-- should improve this by expanding the bounding box or something instead!
|
||||
-- create a larger bounding box and then planes for each side, and check distance from the plane
|
||||
-- separate function perhaps?
|
||||
local distance = (LocalPlayer():GetPos() - target.entity:GetPos()):Length() - (obvgrid.unw - obvgrid.lse):Length()
|
||||
if (distance > GetConVarNumber("snap_distance")) then return end
|
||||
|
||||
for face, vertices in ipairs(faces) do
|
||||
intersection = RayQuadIntersect(cache.vLookPos, cache.vLookVector, vertices[1], vertices[2], vertices[3])
|
||||
|
||||
if (intersection) then
|
||||
target.face = face
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if intersection == nil then return end
|
||||
end
|
||||
|
||||
if (GetConVarNumber("snap_boundingbox") ~= 0) then
|
||||
DrawBoundary(obvgrid.unw, obvgrid.lnw, obvgrid.usw, obvgrid.une)
|
||||
DrawBoundary(obvgrid.une, obvgrid.lne, obvgrid.use, obvgrid.unw)
|
||||
DrawBoundary(obvgrid.lnw, obvgrid.unw, obvgrid.lsw, obvgrid.lne)
|
||||
DrawBoundary(obvgrid.lne, obvgrid.une, obvgrid.lse, obvgrid.lnw)
|
||||
DrawBoundary(obvgrid.usw, obvgrid.lsw, obvgrid.unw, obvgrid.use)
|
||||
DrawBoundary(obvgrid.use, obvgrid.lse, obvgrid.une, obvgrid.usw)
|
||||
DrawBoundary(obvgrid.lsw, obvgrid.usw, obvgrid.lnw, obvgrid.lse)
|
||||
DrawBoundary(obvgrid.lse, obvgrid.use, obvgrid.lne, obvgrid.lsw)
|
||||
end
|
||||
|
||||
local vectorOrigin = faces[target.face][1]
|
||||
local vectorX = faces[target.face][2]
|
||||
local vectorY = faces[target.face][3]
|
||||
local vectorZ = faces[target.face][4]
|
||||
local vectorOffset = faces[target.face][5]
|
||||
local vectorGrid
|
||||
|
||||
if (not target.locked) then
|
||||
vectorGrid = vectorOrigin + CalculateSnap(vectorX, vectorY, intersection)
|
||||
|
||||
local trace = util.TraceLine({
|
||||
start = target.entity:LocalToWorld(target.entity:WorldToLocal(vectorGrid) - vectorOffset) - vectorZ:GetNormalized() * 0.01,
|
||||
endpos = vectorGrid + vectorZ
|
||||
})
|
||||
|
||||
local vectorSnap = trace.HitPos
|
||||
target.offset = target.entity:WorldToLocal(vectorSnap)
|
||||
target.vector = target.entity:WorldToLocal(vectorGrid)
|
||||
target.error = true
|
||||
|
||||
if (trace.Entity == nil or not trace.Entity:IsValid()) then
|
||||
snaperror = -1
|
||||
elseif (trace.Entity ~= target.entity) then
|
||||
snaperror = -2
|
||||
elseif (trace.HitPos == trace.StartPos) then
|
||||
snaperror = -2
|
||||
else
|
||||
snaperror = (LocalPlayer():GetEyeTrace().HitPos - trace.HitPos):Length()
|
||||
target.error = false
|
||||
|
||||
if ((vectorSnap - vectorGrid):Length() > 0.5) then
|
||||
local marker = PointToScreen(vectorSnap)
|
||||
|
||||
if (marker) then
|
||||
DrawSnapCross(marker, 255, 255, 255)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
vectorGrid = target.entity:LocalToWorld(target.vector)
|
||||
local vectorSnap = target.entity:LocalToWorld(target.offset)
|
||||
local marker = PointToScreen(vectorSnap)
|
||||
snaperror = (LocalPlayer():GetEyeTrace().HitPos - vectorSnap):Length()
|
||||
|
||||
if (marker) then
|
||||
if (target.error == true) then
|
||||
snaperror = -2
|
||||
DrawSnapCross(marker, 0, 255, 255)
|
||||
elseif (snaperror < 0.001) then
|
||||
DrawSnapCross(marker, 0, 255, 0)
|
||||
elseif (snaperror < 0.1) then
|
||||
DrawSnapCross(marker, 255, 255, 0)
|
||||
else
|
||||
DrawSnapCross(marker, 255, 0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (GetConVarNumber("snap_hidegrid") == 0) then
|
||||
DrawGrid(vectorOrigin, vectorX, vectorY)
|
||||
end
|
||||
|
||||
target.active = true
|
||||
local vsCursor = PointToScreen(vectorGrid)
|
||||
|
||||
if (vsCursor) then
|
||||
if (snaperror == -1) then
|
||||
target.active = false
|
||||
DrawSnapCross(vsCursor, 0, 255, 255)
|
||||
elseif (snaperror == -2) then
|
||||
DrawSnapCross(vsCursor, 255, 0, 255)
|
||||
elseif (snaperror < 0.001) then
|
||||
DrawSnapCross(vsCursor, 0, 255, 0)
|
||||
elseif (snaperror < 0.1) then
|
||||
DrawSnapCross(vsCursor, 255, 255, 0)
|
||||
else
|
||||
DrawSnapCross(vsCursor, 255, 0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnSnapView(player, origin, angles, fov)
|
||||
local targetvalid = target.active and target.locked and target.entity:IsValid()
|
||||
local snaptargetvalid = snaptarget.active and snaptarget.locked and snaptarget.entity:IsValid()
|
||||
|
||||
if (snapcursor and not snapspawnmenu and targetvalid) then
|
||||
local screen = ToScreen(target.entity:LocalToWorld(target.offset))
|
||||
gui.SetMousePos(math.Round(screen.x), math.Round(screen.y))
|
||||
end
|
||||
|
||||
if (not snapcursor and targetvalid) then
|
||||
return {
|
||||
angles = (target.entity:LocalToWorld(target.offset) - player:GetShootPos()):Angle()
|
||||
}
|
||||
elseif (snaplock and snaptargetvalid) then
|
||||
return {
|
||||
angles = (snaptarget.entity:LocalToWorld(snaptarget.offset) - player:GetShootPos()):Angle()
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function OnSnapAim(user)
|
||||
local targetvalid = target.active and target.locked and target.entity:IsValid()
|
||||
local snaptargetvalid = snaptarget.active and snaptarget.locked and snaptarget.entity:IsValid()
|
||||
|
||||
if (not snapcursor and targetvalid) then
|
||||
user:SetViewAngles((target.entity:LocalToWorld(target.offset) - LocalPlayer():GetShootPos()):Angle())
|
||||
elseif (snaplock and snaptargetvalid) then
|
||||
user:SetViewAngles((snaptarget.entity:LocalToWorld(snaptarget.offset) - LocalPlayer():GetShootPos()):Angle())
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("+snap", SnapPress)
|
||||
concommand.Add("-snap", SnapRelease)
|
||||
concommand.Add("snaplock", SnapLock)
|
||||
concommand.Add("snaptogglegrid", SnapToggleGrid)
|
||||
hook.Add("Initialize", "SmartsnapInitialize", OnInitialize)
|
||||
hook.Add("SpawnMenuOpen", "SmartsnapSpawnMenu", OnSpawnMenu)
|
||||
hook.Add("Think", "SmartsnapThink", OnThink)
|
||||
hook.Add("ShutDown", "SmartsnapShutDown", OnShutDown)
|
||||
hook.Add("KeyPress", "SmartsnapKeyPress", OnKeyPress)
|
||||
hook.Add("KeyRelease", "SmartsnapKeyRelease", OnKeyRelease)
|
||||
hook.Add("CreateMove", "SmartsnapSnap", OnSnapAim)
|
||||
hook.Add("CalcView", "SmartsnapSnapView", OnSnapView)
|
||||
hook.Add("SpawnMenuOpen", "SmartsnapSpawnMenu", OnSpawnMenu)
|
||||
hook.Add("HUDPaintBackground", "SmartsnapPaintHUD", OnPaintHUD)
|
||||
|
||||
local function OnPopulateToolPanel(panel)
|
||||
panel:AddControl("ComboBox", {
|
||||
Options = {
|
||||
["default"] = condefs
|
||||
},
|
||||
CVars = convars,
|
||||
Label = "",
|
||||
MenuButton = "1",
|
||||
Folder = "smartsnap"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Enable",
|
||||
Command = "snap_enabled"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Use click grid (USE temporarily enables grid)",
|
||||
Command = "snap_clickgrid"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Hide grid (only shows snap point)",
|
||||
Command = "snap_hidegrid"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Smart toggle enabled",
|
||||
Command = "snap_enabletoggle"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Revert aim to grid snap on detach",
|
||||
Command = "snap_revertaim"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Enable for all entities",
|
||||
Command = "snap_allentities"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Enable for all tools",
|
||||
Command = "snap_alltools"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Draw thick center lines",
|
||||
Command = "snap_centerline"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Grid toggle delay (double click snap-key)",
|
||||
Command = "snap_toggledelay",
|
||||
Type = "Float",
|
||||
Min = "0.0",
|
||||
Max = "0.2"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Smart lock delay",
|
||||
Command = "snap_lockdelay",
|
||||
Type = "Float",
|
||||
Min = "0.0",
|
||||
Max = "5.0"
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Bounding box enabled",
|
||||
Command = "snap_boundingbox"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Grid draw distance",
|
||||
Command = "snap_distance",
|
||||
Type = "Integer",
|
||||
Min = "50",
|
||||
Max = "1000"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Grid edge offset",
|
||||
Command = "snap_gridoffset",
|
||||
Type = "Float",
|
||||
Min = "0.0",
|
||||
Max = "2.5"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Grid transparency",
|
||||
Command = "snap_gridalpha",
|
||||
Type = "Float",
|
||||
Min = "0.1",
|
||||
Max = "1.0"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Maximum number of snap points on an axis",
|
||||
Command = "snap_gridlimit",
|
||||
Type = "Integer",
|
||||
Min = "2",
|
||||
Max = "64"
|
||||
})
|
||||
|
||||
panel:AddControl("Slider", {
|
||||
Label = "Minimum distance between each snap point",
|
||||
Command = "snap_gridsize",
|
||||
Type = "Integer",
|
||||
Min = "2",
|
||||
Max = "64"
|
||||
})
|
||||
|
||||
panel:AddControl("Label", {
|
||||
Text = ""
|
||||
})
|
||||
|
||||
panel:AddControl("Label", {
|
||||
Text = "The following option should prevent FPS drops from occuring, however it might have a slight impact on the average FPS while the grid is showing. Do NOT uncheck this option unless you are experiencing very low FPS or fully understands its purpose."
|
||||
})
|
||||
|
||||
panel:AddControl("Label", {
|
||||
Text = "NOTE: This option is only effective when the grid is showing, it does not impact regular gameplay!"
|
||||
})
|
||||
|
||||
panel:AddControl("Label", {
|
||||
Text = ""
|
||||
})
|
||||
|
||||
panel:AddControl("CheckBox", {
|
||||
Label = "Garbage collection boost",
|
||||
Command = "snap_gcboost"
|
||||
})
|
||||
end
|
||||
|
||||
function OnPopulateToolMenu()
|
||||
spawnmenu.AddToolMenuOption("Options", "Player", "SmartSnapSettings", "SmartSnap", "", "", OnPopulateToolPanel, {
|
||||
SwitchConVar = 'snap_enabled'
|
||||
})
|
||||
end
|
||||
|
||||
hook.Add("PopulateToolMenu", "SmartSnapToolMenu", OnPopulateToolMenu)
|
||||
end
|
||||
Reference in New Issue
Block a user