Initial commit

This commit is contained in:
2026-03-15 14:54:49 +03:00
commit 64f8029c06
4027 changed files with 254888 additions and 0 deletions

188
lua/entities/sent_ball.lua Normal file
View File

@@ -0,0 +1,188 @@
AddCSLuaFile()
DEFINE_BASECLASS( "base_anim" )
ENT.PrintName = "#sent_ball"
ENT.Author = "Garry Newman"
ENT.Information = "An edible bouncy ball. Press USE on the bouncy ball to eat it."
ENT.Category = "#spawnmenu.category.fun_games"
ENT.Editable = true
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.MinSize = 4
ENT.MaxSize = 128
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "BallSize", { KeyName = "ballsize", Edit = { type = "Float", min = self.MinSize, max = self.MaxSize, order = 1 } } )
self:NetworkVar( "Vector", 0, "BallColor", { KeyName = "ballcolor", Edit = { type = "VectorColor", order = 2 } } )
if ( SERVER ) then
self:NetworkVarNotify( "BallSize", self.OnBallSizeChanged )
end
end
-- This is the spawn function. It's called when a client calls the entity to be spawned.
-- If you want to make your SENT spawnable you need one of these functions to properly create the entity
--
-- ply is the name of the player that is spawning it
-- tr is the trace from the player's eyes
--
function ENT:SpawnFunction( ply, tr, ClassName )
if ( !tr.Hit ) then return end
local size = math.random( 16, 48 )
local SpawnPos = tr.HitPos + tr.HitNormal * size
-- Make sure the spawn position is not out of bounds
local oobTr = util.TraceLine( {
start = tr.HitPos,
endpos = SpawnPos,
mask = MASK_SOLID_BRUSHONLY
} )
if ( oobTr.Hit ) then
SpawnPos = oobTr.HitPos + oobTr.HitNormal * ( tr.HitPos:Distance( oobTr.HitPos ) / 2 )
end
local ent = ents.Create( ClassName )
ent:SetPos( SpawnPos )
ent:SetBallSize( size )
ent:Spawn()
ent:Activate()
return ent
end
function ENT:Initialize()
-- We do NOT want to execute anything below in this FUNCTION on CLIENT
if ( CLIENT ) then return end
-- Use the helibomb model just for the shadow (because it's about the same size)
self:SetModel( "models/Combine_Helicopter/helicopter_bomb01.mdl" )
-- We will put this here just in case, even though it should be called from OnBallSizeChanged in any case
self:RebuildPhysics()
-- Set the size if it wasn't set already..
if ( self:GetBallSize() == 0 ) then self:SetBallSize( 32 ) end
-- Select a random color for the ball, if one wasn't set.
if ( !self:GetBallColor():IsZero() ) then return end
self:SetBallColor( table.Random( {
Vector( 1, 0.3, 0.3 ),
Vector( 0.3, 1, 0.3 ),
Vector( 1, 1, 0.3 ),
Vector( 0.2, 0.3, 1 ),
} ) )
end
function ENT:RebuildPhysics( value )
-- This is necessary so that the vphysics.dll will not crash when attaching constraints to the new PhysObj after old one was destroyed
-- TODO: Somehow figure out why it happens and/or move this code/fix to the constraint library
self.ConstraintSystem = nil
local size = math.Clamp( value or self:GetBallSize(), self.MinSize, self.MaxSize ) / 2.1
self:PhysicsInitSphere( size, "metal_bouncy" )
self:SetCollisionBounds( Vector( -size, -size, -size ), Vector( size, size, size ) )
self:PhysWake()
end
if ( SERVER ) then
function ENT:OnBallSizeChanged( varname, oldvalue, newvalue )
-- Do not rebuild if the size wasn't changed
if ( oldvalue == newvalue ) then return end
self:RebuildPhysics( newvalue )
end
function ENT:KeyValue( key, val )
if ( key == "ball_size" ) then self:SetBallSize( val ) end
if ( key == "rendercolor" ) then self:SetBallColor( Vector( val ) / 255 ) end
end
end
local BounceSound = Sound( "garrysmod/balloon_pop_cute.wav" )
function ENT:PhysicsCollide( data, physobj )
-- Play sound on bounce
if ( data.Speed > 60 && data.DeltaTime > 0.2 ) then
local pitch = 32 + 128 - math.Clamp( self:GetBallSize(), self.MinSize, self.MaxSize )
sound.Play( BounceSound, self:GetPos(), 75, math.random( pitch - 10, pitch + 10 ), math.Clamp( data.Speed / 150, 0, 1 ) )
end
-- Bounce like a crazy bitch
local LastSpeed = math.max( data.OurOldVelocity:Length(), data.Speed )
local NewVelocity = physobj:GetVelocity()
NewVelocity:Normalize()
LastSpeed = math.max( NewVelocity:Length(), LastSpeed )
local TargetVelocity = NewVelocity * LastSpeed * 0.9
physobj:SetVelocity( TargetVelocity )
end
function ENT:OnTakeDamage( dmginfo )
-- React physically when shot/getting blown
self:TakePhysicsDamage( dmginfo )
end
function ENT:Use( activator, caller )
self:Remove()
if ( activator:IsPlayer() ) then
-- Give the collecting player some free health
local health = activator:Health()
activator:SetHealth( health + 5 )
activator:SendLua( "achievements.EatBall()" )
end
end
if ( SERVER ) then return end -- We do NOT want to execute anything below in this FILE on SERVER
local matBall = Material( "sprites/sent_ball" )
function ENT:Draw()
render.SetMaterial( matBall )
local pos = self:GetPos()
local lcolor = render.ComputeLighting( pos, Vector( 0, 0, 1 ) )
local c = self:GetBallColor()
lcolor.x = c.r * ( math.Clamp( lcolor.x, 0, 1 ) + 0.5 ) * 255
lcolor.y = c.g * ( math.Clamp( lcolor.y, 0, 1 ) + 0.5 ) * 255
lcolor.z = c.b * ( math.Clamp( lcolor.z, 0, 1 ) + 0.5 ) * 255
local size = math.Clamp( self:GetBallSize(), self.MinSize, self.MaxSize )
render.DrawSprite( pos, size, size, Color( lcolor.x, lcolor.y, lcolor.z, 255 ) )
end

View File

@@ -0,0 +1,114 @@
AddCSLuaFile()
DEFINE_BASECLASS( "widget_base" )
local matArrow = Material( "widgets/arrow.png", "nocull alphatest smooth mips" )
local matScale = Material( "widgets/scale.png", "nocull alphatest smooth mips" )
function ENT:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Bool", 2, "IsScaleArrow" )
if ( SERVER ) then
self:SetIsScaleArrow( false )
end
end
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
if ( SERVER ) then
self:SetSize( 16 )
end
end
function ENT:PressedShouldDraw( widget ) return widget == self end
function ENT:OverlayRender()
local fwd = self:GetAngles():Forward()
local size = self:GetSize() * 0.5
local c = self:GetColor()
if ( !self:IsHovered() && !self:IsPressed() ) then
c.r = c.r * 0.5
c.g = c.g * 0.5
c.b = c.b * 0.5
end
if ( self:GetIsScaleArrow() ) then
render.SetMaterial( matScale )
else
render.SetMaterial( matArrow )
end
local pos = self:GetPos()
render.DepthRange( 0, 0.01 )
render.DrawBeam( pos, pos + fwd * size, 2, 1, 0, Color( c.r, c.g, c.b, c.a * 0.1 ) )
render.DepthRange( 0, 1 * self:GetPriority() )
render.DrawBeam( pos, pos + fwd * size, 2, 1, 0, c )
render.DepthRange( 0, 1 )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local size = self:GetSize() * 0.5
local mins = Vector( 0, -1, -1 )
local maxs = Vector( size, 1, 1 )
local hit, norm, fraction = util.IntersectRayWithOBB( startpos, delta, self:GetPos(), self:GetAngles(), mins, maxs )
if ( !hit ) then return end
--debugoverlay.BoxAngles( self:GetPos(), mins, maxs, self:GetAngles(), 0.1, Color( 0, 0, 255, 64 ) )
return {
HitPos = hit,
Fraction = fraction * self:GetPriority()
}
end
function ENT:DragThink( pl, mv, dist )
local d = dist.x * -1
self:ArrowDragged( pl, mv, d )
end
function ENT:ArrowDragged( pl, mv, dist )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local arrowdir = self:GetAngles():Forward()
local planepos = self:GetPos()
local planenrm = ( eye - planepos ):GetNormalized()
local hitpos = util.IntersectRayWithPlane( eye, fwd, planepos, planenrm )
if ( !hitpos ) then return end
-- Get nearest point along the arrow where we touched it
local fdist, vpos, falong = util.DistanceToLine( planepos - arrowdir * 1024, planepos + arrowdir * 1024, hitpos )
return vpos
end

View File

@@ -0,0 +1,121 @@
AddCSLuaFile()
--
-- widget_axis_arrow
--
DEFINE_BASECLASS( "widget_arrow" )
local widget_axis_arrow = { Base = "widget_arrow" }
function widget_axis_arrow:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Int", 0, "AxisIndex" )
end
function widget_axis_arrow:ArrowDragged( pl, mv, dist )
self:GetParent():OnArrowDragged( self:GetAxisIndex(), dist, pl, mv )
end
scripted_ents.Register( widget_axis_arrow, "widget_axis_arrow" )
--
-- widget_axis_disc
--
DEFINE_BASECLASS( "widget_disc" )
local widget_axis_disc = { Base = "widget_disc" }
function widget_axis_disc:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Int", 0, "AxisIndex" )
end
function widget_axis_disc:ArrowDragged( pl, mv, dist )
self:GetParent():OnArrowDragged( self:GetAxisIndex(), dist, pl, mv )
end
scripted_ents.Register( widget_axis_disc, "widget_axis_disc" )
DEFINE_BASECLASS( "widget_base" )
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetSolid( SOLID_NONE )
end
function ENT:Setup( ent, boneid, rotate )
self:FollowBone( ent, boneid )
self:SetLocalPos( vector_origin )
self:SetLocalAngles( angle_zero )
local EntName = "widget_axis_arrow"
if ( rotate ) then EntName = "widget_axis_disc" end
self.ArrowX = ents.Create( EntName )
self.ArrowX:SetParent( self )
self.ArrowX:SetColor( Color( 255, 0, 0, 255 ) )
self.ArrowX:Spawn()
self.ArrowX:SetLocalPos( vector_origin )
self.ArrowX:SetLocalAngles( Vector( 1, 0, 0 ):Angle() )
self.ArrowX:SetAxisIndex( 1 )
self.ArrowY = ents.Create( EntName )
self.ArrowY:SetParent( self )
self.ArrowY:SetColor( Color( 0, 230, 50, 255 ) )
self.ArrowY:Spawn()
self.ArrowY:SetLocalPos( vector_origin )
self.ArrowY:SetLocalAngles( Vector( 0, 1, 0 ):Angle() )
self.ArrowY:SetAxisIndex( 2 )
self.ArrowZ = ents.Create( EntName )
self.ArrowZ:SetParent( self )
self.ArrowZ:SetColor( Color( 50, 100, 255, 255 ) )
self.ArrowZ:Spawn()
self.ArrowZ:SetLocalPos( vector_origin )
self.ArrowZ:SetLocalAngles( Vector( 0, 0, 1 ):Angle() )
self.ArrowZ:SetAxisIndex( 3 )
if ( self.IsScaleArrow && EntName == "widget_axis_arrow" ) then
if ( IsValid( self.ArrowX ) ) then self.ArrowX:SetIsScaleArrow( true ) end
if ( IsValid( self.ArrowY ) ) then self.ArrowY:SetIsScaleArrow( true ) end
if ( IsValid( self.ArrowZ ) ) then self.ArrowZ:SetIsScaleArrow( true ) end
end
end
function ENT:SetPriority( x )
if ( IsValid( self.ArrowX ) ) then self.ArrowX:SetPriority( x ) end
if ( IsValid( self.ArrowY ) ) then self.ArrowY:SetPriority( x ) end
if ( IsValid( self.ArrowZ ) ) then self.ArrowZ:SetPriority( x ) end
end
function ENT:Draw()
end
function ENT:OverlayRender()
end
function ENT:OnArrowDragged( num, dist, pl, mv )
end

View File

@@ -0,0 +1,238 @@
AddCSLuaFile()
DEFINE_BASECLASS( "base_anim" )
ENT.Spawnable = false
ENT.AdminOnly = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.Widget = true
-- This appears to be unused in the base game, but leaving it in case someones does
ENT.Materials = {}
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "SizeVar" ) -- Size (bounds)
self:NetworkVar( "Float", 1, "Priority" ) -- Priority above other widgets (clicks, visually)
end
--
-- Unfilled, for override. Self explanatory.
-- Called on both client and server in multiplayer
-- Only on the server in singleplayer.
--
function ENT:OnClick( ply ) end
function ENT:OnRightClick( ply ) end
function ENT:PressedThink( pl, mv ) end
function ENT:PressedShouldDraw( widget ) return true end
function ENT:PressStart( pl, mv ) end
function ENT:PressEnd( pl, mv ) end
function ENT:DragThink( pl, mv, dist ) end
--
-- Set our dimensions etc
--
function ENT:Initialize()
self:SetCollisionGroup( COLLISION_GROUP_WORLD )
self:DrawShadow( false )
self:EnableCustomCollisions()
self.Color = Color( 0, 255, 255 )
self.Color_Hover = color_white
if ( SERVER ) then
self:SetSize( 4 )
self:SetPriority( 1 )
end
end
function ENT:GetSize()
return self:GetSizeVar()
end
function ENT:SetSize( size )
if ( self:GetSize() == size ) then return end
local sizeVector = Vector( size, size, size )
self:SetSizeVar( size )
self:SetSolid( SOLID_BBOX )
self:SetCollisionBounds( sizeVector * -0.5, sizeVector * 0.5 )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local planepos = self:GetPos()
local planenrm = ( eye - planepos ):GetNormalized()
return util.IntersectRayWithPlane( eye, fwd, planepos, planenrm )
end
function ENT:PressedThinkInternal( ply, mv )
local eyePos = ply:EyePos()
local aimVector = ply:GetAimVector()
--
-- TODO: We should find out why this happens instead of just preventing it!
--
if ( !istable( ply.WidgetMove ) ) then
ply.WidgetMove = {}
ply.WidgetMove.EyePos = eyePos
ply.WidgetMove.EyeVec = aimVector
end
local widgetMove = ply.WidgetMove
local OldPos = self:GetGrabPos( widgetMove.EyePos, widgetMove.EyeVec )
local NewPos = self:GetGrabPos( eyePos, aimVector )
if ( NewPos && OldPos ) then
local dist = self:WorldToLocal( OldPos ) - self:WorldToLocal( NewPos )
local len = dist:LengthSqr()
local minLen = 0.01 * 0.01
local maxLen = 512 * 512
if ( len > minLen && len < maxLen ) then
self:DragThink( ply, mv, dist )
end
end
self:PressedThink( ply, mv )
-- Store the (new) old eye positions
widgetMove.EyePos = eyePos
widgetMove.EyeVec = aimVector
end
--
-- Called by widget's Tick hook when a mouse button was
-- pressed while hovering over this widget
--
function ENT:OnPress( ply, iButton, mv )
if ( self.Pressed ) then return end
ply:SetPressedWidget( self )
ply.WidgetMove = {}
ply.WidgetMove.EyePos = ply:EyePos()
ply.WidgetMove.EyeVec = ply:GetAimVector()
self:PressStart( ply, mv )
end
--
-- Called by widget's Tick hook when a mouse button was
-- released while this widget is pressed
--
function ENT:OnRelease( ply, iButton, mv )
ply:SetPressedWidget( NULL )
ply.WidgetMove = nil
self:PressEnd( ply, mv )
--
-- The player has to click and release on the widget
-- or we assume they clicked and changed their mind
-- so dragged off.. like people do sometimes.
--
if ( ply:GetHoveredWidget() != self ) then return end
--
-- Left Mouse
--
if ( iButton == 1 ) then
self:OnClick( ply )
end
--
-- Right Mouse
--
if ( iButton == 1 ) then
self:OnRightClick( ply )
end
end
function ENT:IsHovered()
return LocalPlayer():GetHoveredWidget() == self
end
function ENT:SomethingHovered()
return IsValid( LocalPlayer():GetHoveredWidget() )
end
function ENT:IsPressed()
return LocalPlayer():GetPressedWidget() == self
end
function ENT:Draw()
widgets.RenderMe( self )
end
local colDefault = Color( 0, 0, 50, 255 )
local colHovered = Color( 20, 50, 100, 255 )
local colPressed = Color( 180, 180, 50, 255 )
local colHoveredAndPressed = Color( 255, 255, 100, 255 )
function ENT:OverlayRender()
local col = colDefault
local ply = LocalPlayer()
local hovered = ply:GetHoveredWidget()
if ( hovered == self ) then
col = colHovered
end
if ( self:IsPressed() ) then
col = colPressed
if ( hovered == ply:GetPressedWidget() ) then
col = colHoveredAndPressed
end
end
local size = self:GetSize()
local vSize = Vector( size, size, size )
local pos = self:GetPos()
local angles = self:GetAngles()
render.SetColorMaterialIgnoreZ()
render.DrawBox( pos, angles, -vSize, vSize, ColorAlpha( col, 0.8 ) )
render.SetColorMaterial()
render.DrawBox( pos, angles, -vSize, vSize, col )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
-- TODO. Actually trace against our cube!
return {
HitPos = self:GetPos(),
Fraction = 0.5 * self:GetPriority()
}
end

View File

@@ -0,0 +1,176 @@
AddCSLuaFile()
local matBone = Material( "widgets/bone.png", "smooth" )
local matBoneSmall = Material( "widgets/bone_small.png", "smooth" )
local c = Color( 255, 255, 255, 255 )
local widget_bone = {
Base = "widget_base",
a = 255,
GetParentPos = function( self )
local p = self:GetParent()
if ( !IsValid( p ) ) then return end
local bp = p:GetBoneParent( self:GetParentAttachment() )
if ( bp <= 0 ) then return end
return p:GetBonePosition( bp )
end,
OverlayRender = function( self )
local pp = self:GetParentPos()
if ( !pp ) then return end
local len = self:GetSize() / 2
local w = len * 0.2
if ( len > 10 ) then
render.SetMaterial( matBone )
else
render.SetMaterial( matBoneSmall )
end
local hovered = LocalPlayer():GetHoveredWidget()
if ( IsValid( hovered ) && hovered != self ) then
self.a = math.Approach( self.a, 20, FrameTime() * 255 * 10 )
else
self.a = math.Approach( self.a, 255, FrameTime() * 255 * 10 )
end
local pos = self:GetPos()
cam.IgnoreZ( true )
c.a = self.a * 0.5
render.DrawBeam( pos, pp, w, 0, 1, c )
cam.IgnoreZ( false )
c.a = self.a
render.DrawBeam( pos, pp, w, 0, 1, c )
end,
TestCollision = function( self, startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local pp = self:GetParentPos()
if ( !pp ) then return end
local pos = self:GetPos()
local fwd = ( pp - pos ):GetNormal()
local ang = fwd:Angle()
local len = self:GetSize() / 2
local w = len * 0.2
local mins = Vector( 0, w * -0.5, w * -0.5 )
local maxs = Vector( len, w * 0.5, w * 0.5 )
local hit, norm, fraction = util.IntersectRayWithOBB( startpos, delta, pos, ang, mins, maxs )
if ( !hit ) then return end
--debugoverlay.BoxAngles( self:GetPos(), mins, maxs, ang, 0.1, Color( 0, 255, 0, 64 ) )
return {
HitPos = hit,
Fraction = fraction * self:GetPriority()
}
end,
Think = function( self )
if ( !SERVER ) then return end
local parent = self:GetParent()
if ( !IsValid( parent ) ) then return end
--
-- Because the bone length changes (with the manipulator)
-- we need to update the bones pretty regularly
--
local size = parent:BoneLength( self:GetParentAttachment() ) * 2
size = math.ceil( size )
self:SetSize( size )
end,
CalcAbsolutePosition = function( self, v, a )
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
return ent:GetBonePosition( bone )
end
}
scripted_ents.Register( widget_bone, "widget_bone" )
DEFINE_BASECLASS( "widget_base" )
function ENT:SetupDataTables()
self:NetworkVar( "Entity", 0, "Target" )
self.BaseClass.SetupDataTables( self )
end
--
-- Set our dimensions etc
--
function ENT:Initialize()
self.BaseClass.Initialize( self )
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetSolid( SOLID_NONE )
end
function ENT:Setup( ent )
self:SetTarget( ent )
self:SetParent( ent )
self:SetLocalPos( vector_origin )
for k = 0, ent:GetBoneCount() - 1 do
if ( ent:GetBoneParent( k ) <= 0 ) then continue end
if ( !ent:BoneHasFlag( k, BONE_USED_BY_VERTEX_LOD0 ) ) then continue end
local btn = ents.Create( "widget_bone" )
btn:FollowBone( ent, k )
btn:SetLocalPos( vector_origin )
btn:SetLocalAngles( angle_zero )
btn:Spawn()
btn:SetSize( ent:BoneLength( k ) * 2 )
btn.OnClick = function( x, ply )
self:OnBoneClick( k, ply )
end
self:DeleteOnRemove( btn )
end
end
function ENT:OnBoneClick( boneid, ply )
end
function ENT:OverlayRender()
end

View File

@@ -0,0 +1,111 @@
AddCSLuaFile()
local matDisc = Material( "widgets/disc.png", "nocull alphatest smooth mips" )
local matDiscAlpha = Material( "widgets/disc.png", "nocull smooth mips" )
DEFINE_BASECLASS( "widget_arrow" )
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
if ( SERVER ) then
self:SetSize( 16 )
end
end
function ENT:PressedShouldDraw( widget ) return widget == self end
function ENT:OverlayRender()
local angles = self:GetAngles()
local fwd = angles:Forward()
local size = self:GetSize()
local c = self:GetColor()
if ( !self:IsHovered() && !self:IsPressed() ) then
c.r = c.r * 0.5
c.g = c.g * 0.5
c.b = c.b * 0.5
end
local pos = self:GetPos()
local ang = angles.roll - 90
render.DepthRange( 0, 0.01 )
render.SetMaterial( matDiscAlpha )
render.DrawQuadEasy( pos, fwd, size, size, Color( c.r, c.g, c.b, c.a * 0.2 ), ang )
render.DepthRange( 0, 1 )
render.SetMaterial( matDisc )
render.DrawQuadEasy( pos, fwd, size, size, c, ang )
render.DepthRange( 0, 1 )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local fwd = self:GetAngles():Forward()
local size = self:GetSize() * 0.5
local pos = self:GetPos()
local hitpos = util.IntersectRayWithPlane( startpos, delta:GetNormalized(), pos, fwd )
if ( !hitpos ) then return end
local dist = pos:DistToSqr( hitpos )
size = size * size
if ( dist > size ) then return end
if ( dist < size * 0.9 ) then return end
--debugoverlay.Cross( hitpos, 0.5, 60 )
local fraction = ( hitpos - startpos ):Length() / delta:Length()
return {
HitPos = hitpos,
Fraction = fraction * self:GetPriority()
}
end
function ENT:DragThink( pl, mv, dist )
local d = dist.x * -1
self:ArrowDragged( pl, mv, d )
end
function ENT:ArrowDragged( pl, mv, dist )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local arrowdir = self:GetAngles():Forward()
local planepos = self:GetPos()
--local planenrm = ( eye - planepos ):GetNormal()
local hitpos = util.IntersectRayWithPlane( eye, fwd, planepos, arrowdir )
if ( !hitpos ) then return end
-- The whole circle should be 360
hitpos = self:WorldToLocal( hitpos )
hitpos:Normalize()
local angle = math.atan2( hitpos.y, hitpos.z ) + math.pi
angle = math.deg( angle ) * -1
return arrowdir * angle
end