-
Notifications
You must be signed in to change notification settings - Fork 836
Expand file tree
/
Copy pathproperties.lua
More file actions
254 lines (167 loc) · 6.69 KB
/
properties.lua
File metadata and controls
254 lines (167 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
module( "properties", package.seeall )
local meta = {
MsgStart = function( self )
net.Start( "properties" )
net.WriteString( self.InternalName )
end,
MsgEnd = function( self )
net.SendToServer()
end
}
meta.__index = meta
List = {}
-- .MenuLabel [string] Label to show on opened menu
-- .Filter( ent ) [function] Return true if we should show a menu for this entity
-- .Action( ent ) [function] On menu choice selected
-- .Order [int] The order in which it should be shown on the menu
-- .Receive( len, ply ) [function] A message has been received from the clientside version
function Add( name, tab )
name = name:lower()
tab.InternalName = name
setmetatable( tab, meta )
List[ name ] = tab
end
function Remove( name )
if ( !name || name == "" ) then return end
name = name:lower()
if ( !List[ name ] ) then return end
List[ name ] = nil
end
local function AddToggleOption( data, menu, ent, ply, tr )
if ( !menu.ToggleSpacer ) then
menu.ToggleSpacer = menu:AddSpacer()
menu.ToggleSpacer:SetZPos( 500 )
end
local option = menu:AddOption( data.MenuLabel, function() data:Action( ent, tr ) end )
option:SetChecked( data:Checked( ent, ply ) )
option:SetZPos( 501 )
return option
end
local function AddOption( data, menu, ent, ply, tr )
if ( data.Type == "toggle" ) then return AddToggleOption( data, menu, ent, ply, tr ) end
if ( data.PrependSpacer ) then
menu:AddSpacer()
end
local option = menu:AddOption( data.MenuLabel, function() data:Action( ent, tr ) end )
if ( data.MenuIcon ) then
option:SetImage( data.MenuIcon )
end
if ( data.MenuOpen ) then
data.MenuOpen( data, option, ent, tr )
end
return option
end
function OpenEntityMenu( ent, tr )
local menu = DermaMenu()
for k, v in SortedPairsByMemberValue( List, "Order" ) do
if ( !v.Filter ) then continue end
if ( !v:Filter( ent, LocalPlayer() ) ) then continue end
local option = AddOption( v, menu, ent, LocalPlayer(), tr )
if ( v.OnCreate ) then v:OnCreate( menu, option ) end
end
menu:Open()
end
function OnScreenClick( eyepos, eyevec )
local ent, tr = GetHovered( eyepos, eyevec )
if ( !IsValid( ent ) ) then return end
OpenEntityMenu( ent, tr )
end
-- Use this check in your properties to see if given entity can be affected by it
-- Ideally this should be done automatically for you, but due to how this system was set up, its now impossible
function CanBeTargeted( ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
-- Check the range if player object is given
-- This is not perfect, but it is close enough and its definitely better than nothing
if ( IsValid( ply ) ) then
local mins = ent:OBBMins()
local maxs = ent:OBBMaxs()
local maxRange = math.max( math.abs( mins.x ) + maxs.x, math.abs( mins.y ) + maxs.y, math.abs( mins.z ) + maxs.z )
local pos = ent:LocalToWorld( ent:OBBCenter() ) --ent:GetPos()
if ( pos:Distance( ply:GetShootPos() ) > maxRange + 1024 ) then return false end
end
return !( ent:GetPhysicsObjectCount() < 1 && ent:GetSolid() == SOLID_NONE && bit.band( ent:GetSolidFlags(), FSOLID_USE_TRIGGER_BOUNDS ) == 0 && bit.band( ent:GetSolidFlags(), FSOLID_CUSTOMRAYTEST ) == 0 )
end
function GetHovered( eyepos, eyevec )
local ply = LocalPlayer()
local filter = ply:GetViewEntity()
if ( filter == ply ) then
local veh = ply:GetVehicle()
if ( veh:IsValid() && ( !veh:IsVehicle() || !veh:GetThirdPersonMode() ) ) then
-- A dirty hack for prop_vehicle_crane. util.TraceLine returns the vehicle but it hits phys_bone_follower - something that needs looking into
filter = { filter, veh, unpack( ents.FindByClass( "phys_bone_follower" ) ) }
end
end
local trace = util.TraceLine( {
start = eyepos,
endpos = eyepos + eyevec * 1024,
filter = filter
} )
-- Hit COLLISION_GROUP_DEBRIS and stuff
if ( !trace.Hit || !IsValid( trace.Entity ) ) then
trace = util.TraceLine( {
start = eyepos,
endpos = eyepos + eyevec * 1024,
filter = filter,
mask = MASK_ALL
} )
end
if ( !trace.Hit || !IsValid( trace.Entity ) ) then return end
return trace.Entity, trace
end
-- Receives commands from clients
if ( SERVER ) then
util.AddNetworkString( "properties" )
net.Receive( "properties", function( len, client )
if ( !IsValid( client ) ) then return end
local name = net.ReadString()
if ( !name ) then return end
local prop = List[ name ]
if ( !prop ) then return end
if ( !prop.Receive ) then return end
prop:Receive( len, client )
end )
end
if ( CLIENT ) then
hook.Add( "PreDrawHalos", "PropertiesHover", function()
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
-- TODO: Remove this hack when every client has MainEyePos
local ent = GetHovered( MainEyePos and MainEyePos() or EyePos(), LocalPlayer():GetAimVector() )
if ( !IsValid( ent ) ) then return end
local c = Color( 255, 255, 255, 255 )
c.r = 200 + math.sin( RealTime() * 50 ) * 55
c.g = 200 + math.sin( RealTime() * 20 ) * 55
c.b = 200 + math.cos( RealTime() * 60 ) * 55
local t = { ent }
if ( ent.GetActiveWeapon && IsValid( ent:GetActiveWeapon() ) ) then table.insert( t, ent:GetActiveWeapon() ) end
halo.Add( t, c, 2, 2, 2, true, false )
end )
hook.Add( "GUIMousePressed", "PropertiesClick", function( code, vector )
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
if ( code == MOUSE_RIGHT && !input.IsButtonDown( MOUSE_LEFT ) ) then
OnScreenClick( MainEyePos and MainEyePos() or EyePos(), vector )
end
end )
local wasPressed = false
hook.Add( "PreventScreenClicks", "PropertiesPreventClicks", function()
if ( !input.IsButtonDown( MOUSE_RIGHT ) ) then wasPressed = false end
if ( wasPressed && input.IsButtonDown( MOUSE_RIGHT ) && !input.IsButtonDown( MOUSE_LEFT ) ) then return true end
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
local ply = LocalPlayer()
if ( !IsValid( ply ) ) then return end
--
-- Are we pressing the right mouse button?
-- (We check whether we're pressing the left too, to allow for physgun freezes)
--
if ( input.IsButtonDown( MOUSE_RIGHT ) && !input.IsButtonDown( MOUSE_LEFT ) ) then
--
-- Are we hovering an entity? If so, then stomp the action
--
local hovered = GetHovered( MainEyePos and MainEyePos() or EyePos(), ply:GetAimVector() )
if ( IsValid( hovered ) ) then
wasPressed = true
return true
end
end
end )
end