mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
568 lines
19 KiB
Lua
568 lines
19 KiB
Lua
--[[
|
|
| 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/
|
|
--]]
|
|
|
|
pace.mctrl = {}
|
|
local mctrl = pace.mctrl
|
|
mctrl.grab_dist = 15
|
|
mctrl.angle_pos = 0.5
|
|
local cvar_pos_grid = CreateClientConVar("pac_grid_pos_size", "4")
|
|
local cvar_ang_grid = CreateClientConVar("pac_grid_ang_size", "45")
|
|
|
|
--[[
|
|
Give this function the coordinates of a pixel on your screen, and it will return a unit vector pointing
|
|
in the direction that the camera would project that pixel in.
|
|
|
|
Useful for converting mouse positions to aim vectors for traces.
|
|
|
|
iScreenX is the x position of your cursor on the screen, in pixels.
|
|
iScreenY is the y position of your cursor on the screen, in pixels.
|
|
iScreenW is the width of the screen, in pixels.
|
|
iScreenH is the height of the screen, in pixels.
|
|
angCamRot is the angle your camera is at
|
|
fFoV is the Field of View (FOV) of your camera in ___radians___
|
|
Note: This must be nonzero or you will get a divide by zero error.
|
|
]]
|
|
local function LPCameraScreenToVector(iScreenX, iScreenY, iScreenW, iScreenH, angCamRot, fFoV)
|
|
--This code works by basically treating the camera like a frustrum of a pyramid.
|
|
--We slice this frustrum at a distance "d" from the camera, where the slice will be a rectangle whose width equals the "4:3" width corresponding to the given screen height.
|
|
local d = 4 * iScreenH / (6 * math.tan(0.5 * fFoV))
|
|
--Forward, right, and up vectors (need these to convert from local to world coordinates
|
|
local vForward = angCamRot:Forward()
|
|
local vRight = angCamRot:Right()
|
|
local vUp = angCamRot:Up()
|
|
--Then convert vec to proper world coordinates and return it
|
|
|
|
return (d * vForward + (iScreenX - 0.5 * iScreenW) * vRight + (0.5 * iScreenH - iScreenY) * vUp):GetNormalized()
|
|
end
|
|
|
|
--[[
|
|
Give this function a vector, pointing from the camera to a position in the world,
|
|
and it will return the coordinates of a pixel on your screen - this is where the world position would be projected onto your screen.
|
|
|
|
Useful for finding where things in the world are on your screen (if they are at all).
|
|
|
|
vDir is a direction vector pointing from the camera to a position in the world
|
|
iScreenW is the width of the screen, in pixels.
|
|
iScreenH is the height of the screen, in pixels.
|
|
angCamRot is the angle your camera is at
|
|
fFoV is the Field of View (FOV) of your camera in ___radians___
|
|
Note: This must be nonzero or you will get a divide by zero error.
|
|
|
|
Returns x, y, iVisibility.
|
|
x and y are screen coordinates.
|
|
iVisibility will be:
|
|
1 if the point is visible
|
|
0 if the point is in front of the camera, but is not visible
|
|
-1 if the point is behind the camera
|
|
]]
|
|
local function VectorToLPCameraScreen(vDir, iScreenW, iScreenH, angCamRot, fFoV)
|
|
--Same as we did above, we found distance the camera to a rectangular slice of the camera's frustrum, whose width equals the "4:3" width corresponding to the given screen height.
|
|
local d = 4 * iScreenH / (6 * math.tan(0.5 * fFoV))
|
|
local fdp = angCamRot:Forward():Dot(vDir)
|
|
--fdp must be nonzero ( in other words, vDir must not be perpendicular to angCamRot:Forward() )
|
|
--or we will get a divide by zero error when calculating vProj below.
|
|
if fdp == 0 then return 0, 0, -1 end
|
|
--Using linear projection, project this vector onto the plane of the slice
|
|
local vProj = (d / fdp) * vDir
|
|
--Dotting the projected vector onto the right and up vectors gives us screen positions relative to the center of the screen.
|
|
--We add half-widths / half-heights to these coordinates to give us screen positions relative to the upper-left corner of the screen.
|
|
--We have to subtract from the "up" instead of adding, since screen coordinates decrease as they go upwards.
|
|
local x = 0.5 * iScreenW + angCamRot:Right():Dot(vProj)
|
|
local y = 0.5 * iScreenH - angCamRot:Up():Dot(vProj)
|
|
--Lastly we have to ensure these screen positions are actually on the screen.
|
|
local iVisibility
|
|
|
|
--Simple check to see if the object is in front of the camera
|
|
if fdp < 0 then
|
|
iVisibility = -1
|
|
elseif x < 0 or x > iScreenW or y < 0 or y > iScreenH then
|
|
--We've already determined the object is in front of us, but it may be lurking just outside our field of vision.
|
|
iVisibility = 0
|
|
else
|
|
iVisibility = 1
|
|
end
|
|
|
|
return x, y, iVisibility
|
|
end
|
|
|
|
local function LocalToWorldAngle(lang, wang)
|
|
local lm = Matrix()
|
|
lm:SetAngles(lang)
|
|
local wm = Matrix()
|
|
wm:SetAngles(wang)
|
|
|
|
return (wm * lm):GetAngles()
|
|
end
|
|
|
|
local function WorldToLocalAngle(lang, wang)
|
|
local lm = Matrix()
|
|
lm:SetAngles(lang)
|
|
local wm = Matrix()
|
|
wm:SetAngles(wang)
|
|
|
|
return (wm:GetInverse() * lm):GetAngles()
|
|
end
|
|
|
|
local function cursor_pos()
|
|
local x, y = input.GetCursorPos()
|
|
|
|
if mctrl.grab and mctrl.grab.mouse_offset then
|
|
x = x + mctrl.grab.mouse_offset.x
|
|
y = y + mctrl.grab.mouse_offset.y
|
|
end
|
|
|
|
return x, y
|
|
end
|
|
|
|
-- pace
|
|
do
|
|
mctrl.target = NULL
|
|
|
|
function mctrl.SetTarget(part)
|
|
part = part or NULL
|
|
|
|
if not part:IsValid() then
|
|
mctrl.target = NULL
|
|
|
|
return
|
|
end
|
|
|
|
if not part.GetDrawPosition then
|
|
mctrl.target = NULL
|
|
else
|
|
mctrl.target = part
|
|
end
|
|
end
|
|
|
|
function mctrl.GetTarget()
|
|
return mctrl.target:IsValid() and not mctrl.target:IsHidden() and mctrl.target or NULL
|
|
end
|
|
|
|
function mctrl.GetAxes(ang)
|
|
return ang:Forward(), ang:Right() * -1, ang:Up()
|
|
end
|
|
|
|
function mctrl.GetWorldPosition()
|
|
local part = mctrl.GetTarget()
|
|
if not part:IsValid() then return end
|
|
local m = part:GetWorldMatrixWithoutOffsets()
|
|
|
|
return m:GetTranslation(), m:GetAngles()
|
|
end
|
|
|
|
function mctrl.GetWorldMatrix()
|
|
local part = mctrl.GetTarget()
|
|
if not part:IsValid() then return end
|
|
|
|
return part:GetWorldMatrixWithoutOffsets()
|
|
end
|
|
|
|
function mctrl.WorldToLocalPosition(pos, ang)
|
|
local part = mctrl.GetTarget()
|
|
if not part:IsValid() then return end
|
|
local wpos, wang = part:GetBonePosition()
|
|
if wpos and wang then return WorldToLocal(pos, ang, wpos, wang) end
|
|
end
|
|
|
|
function mctrl.GetCameraFOV()
|
|
if pace.editing_viewmodel or pace.editing_hands then return pac.LocalPlayer:GetActiveWeapon().ViewModelFOV or 55 end
|
|
|
|
return pace.GetViewFOV()
|
|
end
|
|
|
|
function mctrl.VecToScreen(vec)
|
|
local x, y, vis = VectorToLPCameraScreen((vec - EyePos()):GetNormalized(), ScrW(), ScrH(), EyeAngles(), math.rad(mctrl.GetCameraFOV()))
|
|
|
|
return {
|
|
x = x - 1,
|
|
y = y - 1,
|
|
visible = vis == 1
|
|
}
|
|
end
|
|
|
|
function mctrl.ScreenToVec(x, y)
|
|
local vec = LPCameraScreenToVector(x, y, ScrW(), ScrH(), EyeAngles(), math.rad(mctrl.GetCameraFOV()))
|
|
|
|
return vec
|
|
end
|
|
|
|
function mctrl.GetGizmoSize()
|
|
local part = pace.current_part
|
|
if pace.editing_viewmodel or pace.editing_hands then return 5 end
|
|
|
|
if part.ClassName == "clip" or part.ClassName == "clip2" then
|
|
part = part.Parent
|
|
end
|
|
|
|
if part.ClassName == "camera" then return 30 end
|
|
if part.ClassName == "group" then return 45 end
|
|
if not part:IsValid() or not part.GetWorldPosition then return 3 end
|
|
local dist = (part:GetWorldMatrixWithoutOffsets():GetTranslation():Distance(pace.GetViewPos()) / 50)
|
|
|
|
if dist > 1 then
|
|
dist = 1 / dist
|
|
end
|
|
|
|
return 5 * math.rad(pace.GetViewFOV()) / dist
|
|
end
|
|
end
|
|
|
|
function mctrl.LinePlaneIntersection(pos, normal, x, y)
|
|
local n = normal
|
|
local lp = pace.GetViewPos() - pos
|
|
local ln = mctrl.ScreenToVec(x, y)
|
|
|
|
return lp + ln * (-lp:Dot(n) / ln:Dot(n))
|
|
end
|
|
|
|
local function dot2D(x1, y1, x2, y2)
|
|
return x1 * x2 + y1 * y2
|
|
end
|
|
|
|
function mctrl.PointToAxis(pos, axis)
|
|
local x, y = cursor_pos()
|
|
local origin = mctrl.VecToScreen(pos)
|
|
local point = mctrl.VecToScreen(pos + axis * 10)
|
|
local a = math.atan2(point.y - origin.y, point.x - origin.x)
|
|
local d = dot2D(math.cos(a), math.sin(a), point.x - x, point.y - y)
|
|
|
|
return point.x + math.cos(a) * -d, point.y + math.sin(a) * -d
|
|
end
|
|
|
|
function mctrl.CalculateMovement()
|
|
local part = mctrl.GetTarget()
|
|
if not part:IsValid() then return end
|
|
local axis = mctrl.grab.axis
|
|
local offset = mctrl.GetGizmoSize()
|
|
local m = mctrl.grab.matrix --part:GetWorldMatrixWithoutOffsets()
|
|
local pos = m:GetTranslation()
|
|
local forward, right, up = m:GetForward(), -m:GetRight(), m:GetUp()
|
|
local world_dir
|
|
|
|
if axis == "x" then
|
|
local localpos = mctrl.LinePlaneIntersection(pos, right, mctrl.PointToAxis(pos, forward))
|
|
world_dir = (localpos:Dot(forward) - offset) * forward
|
|
elseif axis == "y" then
|
|
local localpos = mctrl.LinePlaneIntersection(pos, forward, mctrl.PointToAxis(pos, right))
|
|
world_dir = (localpos:Dot(right) - offset) * right
|
|
elseif axis == "z" then
|
|
local localpos = mctrl.LinePlaneIntersection(pos, forward, mctrl.PointToAxis(pos, up))
|
|
world_dir = (localpos:Dot(up) - offset) * up
|
|
elseif axis == "view" then
|
|
world_dir = mctrl.LinePlaneIntersection(pos, pace.GetViewAngles():Forward(), cursor_pos())
|
|
end
|
|
|
|
if world_dir then
|
|
local m2 = Matrix()
|
|
m2:SetTranslation(m:GetTranslation() + world_dir)
|
|
local wm = mctrl.grab.bone_matrix:GetInverse() * m2
|
|
local pos = wm:GetTranslation()
|
|
|
|
if input.IsKeyDown(KEY_LCONTROL) then
|
|
local num = cvar_pos_grid:GetInt("pac_grid_pos_size")
|
|
pos.x = math.Round(pos.x / num) * num
|
|
pos.y = math.Round(pos.y / num) * num
|
|
pos.z = math.Round(pos.z / num) * num
|
|
end
|
|
|
|
pace.Call("VariableChanged", part, "Position", pos, 0.25)
|
|
|
|
timer.Create("pace_refresh_properties", 0.1, 1, function()
|
|
pace.PopulateProperties(part)
|
|
end)
|
|
end
|
|
end
|
|
|
|
function mctrl.CalculateRotation()
|
|
local part = mctrl.GetTarget()
|
|
if not part:IsValid() then return end
|
|
local axis = mctrl.grab.axis
|
|
local ang = mctrl.grab.matrix:GetAngles() --part:GetWorldMatrixWithoutOffsets():GetAngles()
|
|
local world_angle
|
|
|
|
if axis == "x" then
|
|
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetRight())
|
|
if not plane_pos then return end
|
|
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
|
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
|
local p = local_angle.p
|
|
|
|
if math.abs(local_angle.y) > 90 then
|
|
p = -p + 180
|
|
end
|
|
|
|
p = math.NormalizeAngle(p)
|
|
world_angle = LocalToWorldAngle(Angle(p, 0, 0), ang)
|
|
elseif axis == "y" then
|
|
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetUp())
|
|
if not plane_pos then return end
|
|
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
|
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
|
world_angle = LocalToWorldAngle(Angle(0, local_angle.y - 90, 0), ang)
|
|
elseif axis == "z" then
|
|
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetForward())
|
|
if not plane_pos then return end
|
|
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
|
diff_angle:RotateAroundAxis(mctrl.grab.matrix:GetForward(), 90)
|
|
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
|
local p = local_angle.p
|
|
|
|
if local_angle.y > 0 then
|
|
p = -p + 180
|
|
end
|
|
|
|
p = math.NormalizeAngle(p)
|
|
world_angle = LocalToWorldAngle(Angle(0, 0, p), ang)
|
|
end
|
|
|
|
if world_angle then
|
|
local ang = WorldToLocalAngle(world_angle, mctrl.grab.bone_matrix:GetAngles())
|
|
|
|
if input.IsKeyDown(KEY_LCONTROL) then
|
|
local num = cvar_ang_grid:GetInt("pac_grid_ang_size")
|
|
ang.p = math.Round(ang.p / num) * num
|
|
ang.y = math.Round(ang.y / num) * num
|
|
ang.r = math.Round(ang.r / num) * num
|
|
end
|
|
|
|
pace.Call("VariableChanged", part, "Angles", ang, 0.25)
|
|
|
|
timer.Create("pace_refresh_properties", 0.1, 1, function()
|
|
pace.PopulateProperties(part)
|
|
end)
|
|
end
|
|
end
|
|
|
|
mctrl.grab = {
|
|
mode = nil,
|
|
axis = nil
|
|
}
|
|
|
|
local GRAB_AND_CLONE = CreateClientConVar("pac_grab_clone", "1", true, false, "Holding shift when moving or rotating a part creates its clone")
|
|
|
|
function mctrl.GUIMousePressed(mc)
|
|
if mc ~= MOUSE_LEFT then return end
|
|
local target = mctrl.GetTarget()
|
|
if not target:IsValid() then return end
|
|
local x, y = input.GetCursorPos()
|
|
local pos, ang = mctrl.GetWorldPosition()
|
|
if not pos or not ang then return end
|
|
local forward, right, up = mctrl.GetAxes(ang)
|
|
local r = mctrl.GetGizmoSize()
|
|
|
|
-- Movement
|
|
do
|
|
local axis
|
|
|
|
for i, v in pairs({
|
|
x = mctrl.VecToScreen(pos + forward * r),
|
|
y = mctrl.VecToScreen(pos + right * r),
|
|
z = mctrl.VecToScreen(pos + up * r),
|
|
view = mctrl.VecToScreen(pos)
|
|
}) do
|
|
local d = math.sqrt((v.x - x) ^ 2 + (v.y - y) ^ 2)
|
|
|
|
if d <= mctrl.grab_dist then
|
|
axis = {
|
|
axis = i,
|
|
pos = v
|
|
}
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
if axis then
|
|
mctrl.grab = {}
|
|
mctrl.grab.mode = "move"
|
|
mctrl.grab.axis = axis.axis
|
|
local x, y = input.GetCursorPos()
|
|
|
|
mctrl.grab.mouse_offset = {
|
|
x = math.ceil(axis.pos.x - x),
|
|
y = math.ceil(axis.pos.y - y),
|
|
}
|
|
|
|
mctrl.grab.matrix = target:GetWorldMatrixWithoutOffsets() * Matrix()
|
|
mctrl.grab.bone_matrix = target:GetBoneMatrix() * Matrix()
|
|
|
|
if GRAB_AND_CLONE:GetBool() and input.IsShiftDown() then
|
|
local copy = target:Clone()
|
|
copy:SetParent(copy:GetParent())
|
|
end
|
|
|
|
pace.RecordUndoHistory()
|
|
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Rotation
|
|
do
|
|
local axis
|
|
|
|
for i, v in pairs({
|
|
x = mctrl.VecToScreen(pos + forward * r * mctrl.angle_pos),
|
|
y = mctrl.VecToScreen(pos + right * r * mctrl.angle_pos),
|
|
z = mctrl.VecToScreen(pos + up * r * mctrl.angle_pos)
|
|
}) do
|
|
local d = math.sqrt((v.x - x) ^ 2 + (v.y - y) ^ 2)
|
|
|
|
if d <= mctrl.grab_dist then
|
|
axis = {
|
|
axis = i,
|
|
pos = v
|
|
}
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
if axis then
|
|
mctrl.grab = {}
|
|
mctrl.grab.mode = "rotate"
|
|
mctrl.grab.axis = axis.axis
|
|
local x, y = input.GetCursorPos()
|
|
|
|
mctrl.grab.mouse_offset = {
|
|
x = math.ceil(axis.pos.x - x) + 0.5,
|
|
y = math.ceil(axis.pos.y - y) + 0.5,
|
|
}
|
|
|
|
mctrl.grab.matrix = target:GetWorldMatrixWithoutOffsets() * Matrix()
|
|
mctrl.grab.bone_matrix = target:GetBoneMatrix() * Matrix()
|
|
mctrl.grab.dist = dist
|
|
|
|
if GRAB_AND_CLONE:GetBool() and input.IsShiftDown() then
|
|
local copy = target:Clone()
|
|
copy:SetParent(copy:GetParent())
|
|
end
|
|
|
|
pace.RecordUndoHistory()
|
|
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
function mctrl.GUIMouseReleased(mc)
|
|
if mc == MOUSE_LEFT then
|
|
mctrl.grab = nil
|
|
end
|
|
|
|
pace.RecordUndoHistory()
|
|
end
|
|
|
|
local white = surface.GetTextureID("gui/center_gradient.vtf")
|
|
|
|
local function DrawLineEx(x1, y1, x2, y2, w, skip_tex)
|
|
w = w or 1
|
|
|
|
if not skip_tex then
|
|
surface.SetTexture(white)
|
|
end
|
|
|
|
local dx, dy = x1 - x2, y1 - y2
|
|
local ang = math.atan2(dx, dy)
|
|
local dst = math.sqrt((dx * dx) + (dy * dy))
|
|
x1 = x1 - dx * 0.5
|
|
y1 = y1 - dy * 0.5
|
|
surface.DrawTexturedRectRotated(x1, y1, w, dst, math.deg(ang))
|
|
end
|
|
|
|
local function DrawLine(x, y, a, b)
|
|
DrawLineEx(x, y, a, b, 3)
|
|
end
|
|
|
|
local function DrawCircleEx(x, y, rad, res, ...)
|
|
res = res or 16
|
|
local spacing = (res / rad) - 0.1
|
|
|
|
for i = 0, res do
|
|
local i1 = ((i + 0) / res) * math.pi * 2
|
|
local i2 = ((i + 1 + spacing) / res) * math.pi * 2
|
|
DrawLineEx(x + math.sin(i1) * rad, y + math.cos(i1) * rad, x + math.sin(i2) * rad, y + math.cos(i2) * rad, ...)
|
|
end
|
|
end
|
|
|
|
function mctrl.LineToBox(origin, point, siz)
|
|
siz = siz or 7
|
|
DrawLine(origin.x, origin.y, point.x, point.y)
|
|
DrawCircleEx(point.x, point.y, siz, 32, 2)
|
|
end
|
|
|
|
function mctrl.RotationLines(pos, dir, dir2, r)
|
|
local pr = mctrl.VecToScreen(pos + dir * r * mctrl.angle_pos)
|
|
local pra = mctrl.VecToScreen(pos + dir * r * (mctrl.angle_pos * 0.9) + dir2 * r * 0.08)
|
|
local prb = mctrl.VecToScreen(pos + dir * r * (mctrl.angle_pos * 0.9) + dir2 * r * -0.08)
|
|
DrawLine(pr.x, pr.y, pra.x, pra.y)
|
|
DrawLine(pr.x, pr.y, prb.x, prb.y)
|
|
end
|
|
|
|
function mctrl.HUDPaint()
|
|
mctrl.LastThinkCall = FrameNumber()
|
|
if pace.IsSelecting then return end
|
|
local target = mctrl.GetTarget()
|
|
if not target then return end
|
|
local pos, ang = mctrl.GetWorldPosition()
|
|
if not pos or not ang then return end
|
|
local forward, right, up = mctrl.GetAxes(ang)
|
|
local radius = mctrl.GetGizmoSize()
|
|
local origin = mctrl.VecToScreen(pos)
|
|
local forward_point = mctrl.VecToScreen(pos + forward * radius)
|
|
local right_point = mctrl.VecToScreen(pos + right * radius)
|
|
local up_point = mctrl.VecToScreen(pos + up * radius)
|
|
|
|
if origin.visible or forward_point.visible or right_point.visible or up_point.visible then
|
|
if mctrl.grab and (mctrl.grab.axis == "x" or mctrl.grab.axis == "view") then
|
|
surface.SetDrawColor(255, 200, 0, 255)
|
|
else
|
|
surface.SetDrawColor(255, 80, 80, 255)
|
|
end
|
|
|
|
mctrl.LineToBox(origin, forward_point)
|
|
mctrl.RotationLines(pos, forward, up, radius)
|
|
|
|
if mctrl.grab and (mctrl.grab.axis == "y" or mctrl.grab.axis == "view") then
|
|
surface.SetDrawColor(255, 200, 0, 255)
|
|
else
|
|
surface.SetDrawColor(80, 255, 80, 255)
|
|
end
|
|
|
|
mctrl.LineToBox(origin, right_point)
|
|
mctrl.RotationLines(pos, right, forward, radius)
|
|
|
|
if mctrl.grab and (mctrl.grab.axis == "z" or mctrl.grab.axis == "view") then
|
|
surface.SetDrawColor(255, 200, 0, 255)
|
|
else
|
|
surface.SetDrawColor(80, 80, 255, 255)
|
|
end
|
|
|
|
mctrl.LineToBox(origin, up_point)
|
|
mctrl.RotationLines(pos, up, right, radius)
|
|
surface.SetDrawColor(255, 200, 0, 255)
|
|
DrawCircleEx(origin.x, origin.y, 4, 32, 2)
|
|
end
|
|
end
|
|
|
|
function mctrl.Think()
|
|
if pace.IsSelecting then return end
|
|
if not mctrl.target:IsValid() then return end
|
|
if not mctrl.grab then return end
|
|
|
|
if mctrl.grab.axis and mctrl.grab.mode == "move" then
|
|
mctrl.CalculateMovement()
|
|
elseif mctrl.grab.axis and mctrl.grab.mode == "rotate" then
|
|
mctrl.CalculateRotation()
|
|
end
|
|
end
|
|
|
|
pac.AddHook("Think", "pace_mctrl_Think", mctrl.Think)
|
|
pace.mctrl = mctrl |