-
Notifications
You must be signed in to change notification settings - Fork 351
Expand file tree
/
Copy pathconstraint.lua
More file actions
322 lines (254 loc) · 10.2 KB
/
constraint.lua
File metadata and controls
322 lines (254 loc) · 10.2 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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
-----------------------------
----Helper Functions--
-----------------------------
-- caps("heLlO") == "Hello"
local function caps(text)
local capstext = text:sub(1,1):upper() .. text:sub(2):lower()
if capstext == "Nocollide" then return "NoCollide" end
if capstext == "Advballsocket" then return "AdvBallsocket" end
return capstext
end
--[[
-- buildFilters
Special keywords for filtering include:
'All'
'Constraints'/'Constraint'
'Parented'/'Parents'/'Parent'
'Wires'/'Wire'
or any constraint type names such as 'Weld', 'Axis', etc
Prefixing any of the keywords (except 'All') with '-' or '!' will negate the filter
Examples
entity():getConnectedEntities("all") => entity():getConnectedEntities() => entity():getConnectedEntities("") => all three are the same
entity():getConnectedEntities("all","-axis") => get all entities except those constrained via axis
entity():getConnectedEntities("weld","parented") => get welded or parented entities
entity():getConnectedEntities("all","-constraint") => is the same as entity():getConnectedEntities("parented")
entity():getConnectedEntities("-weld") => Invalid. Trying to use a negated keyword without also using 'All' or 'Constraints' won't work.
(if an entity is attached to your contraption in multiple ways and one of them match the filter, then it will be returned.)
Keywords are not case sensitive.
]]
local function buildFilter(filters)
local filter_lookup = {}
if #filters == 0 or (#filters == 1 and filters[1] == "") then -- No filters given, same as "All"
filter_lookup.Constraints = true
filter_lookup.Parented = true
filter_lookup.Wires = true
else
for i=1,#filters do
local filter = filters[i]
if type(filter) == "string" then
local bool = true
if string.sub(filter,1,1) == "-" or string.sub(filter,1,1) == "!" then -- check for negation
bool = false
filter = string.sub(filter,2)
end
filter = caps(filter)
-- correct potential mistakes
if filter == "Constraint" then filter = "Constraints"
elseif filter == "Parent" or filter == "Parents" then filter = "Parented"
elseif filter == "Wire" then filter = "Wires" end
if filter == "All" then
if bool then -- "all" can't be negated
filter_lookup.Constraints = true
filter_lookup.Parented = true
filter_lookup.Wires = true
end
else
filter_lookup[filter] = bool
end
end
end
end
return filter_lookup
end
local function checkFilter(constraintType,filter_lookup)
if filter_lookup.Constraints -- check if we allow all constraints
and not (filter_lookup[constraintType] == false) -- but also if this specific constraint hasn't been negated
then return true end
return filter_lookup[constraintType] == true -- check if this specific constraint has been added to the filter
end
-- Custom version of constraint.GetTable, which is faster than garry's and supports filtering
local function constraint_GetTable(ent,filter_lookup)
if not ent.Constraints then return {} end
local result = {}
for _, con in pairs( ent.Constraints ) do
if IsValid(con) then
if filter_lookup and not checkFilter(con.Type,filter_lookup) then continue end
result[#result+1] = con
end
end
return result
end
-- Alias, due to the fact that the custom version of GetTable already supports filtering
local constraint_FindConstraints = constraint_GetTable
-- Custom version of constraint.FindConstraint, which is faster than garry's
local function constraint_FindConstraint(ent,filter_lookup)
if not ent.Constraints then return {} end
for _, con in pairs( ent.Constraints ) do
if IsValid(con) then
if filter_lookup and not checkFilter(con.Type,filter_lookup) then continue end
return con
end
end
end
local getConnectedEntities
-- small helper function for getConnectedEntities
local function getConnectedEx(e, filter_lookup, result, already_added)
if IsValid(e) and not already_added[e] then
getConnectedEntities(e, filter_lookup, result, already_added)
end
end
-- custom version of constraint.GetAllConstrainedEntities, but is faster than garry's
-- and supports filtering and also parented entities
getConnectedEntities = function(ent, filter_lookup, result, already_added)
result = result or {}
already_added = already_added or {}
result[#result+1] = ent
already_added[ent] = true
if filter_lookup then
if filter_lookup.Parented then -- add parented entities
getConnectedEx(ent:GetParent(),filter_lookup, result, already_added)
for _, e in pairs(ent:GetChildren()) do
getConnectedEx( e, filter_lookup, result, already_added )
end
end
if filter_lookup.Wires then -- add wired entities
for _, i in pairs(ent.Inputs or {}) do
getConnectedEx( i.Src, filter_lookup, result, already_added )
end
for _, o in pairs(ent.Outputs or {}) do
getConnectedEx( o.Src, filter_lookup, result, already_added )
end
end
end
for _, con in pairs( ent.Constraints or {} ) do -- add constrained entities
if IsValid(con) then
if filter_lookup and not checkFilter(con.Type,filter_lookup) then -- skip if it doesn't match the filter
continue
end
for i=1, 6 do
getConnectedEx( con["Ent"..i], filter_lookup, result, already_added )
end
end
end
return result
end
--******************************************************************************
__e2setcost(20)
--- Returns an '''array''' containing all entities directly or indirectly constrained to <this>, except <this> itself.
[deprecated, nodiscard]
e2function array entity:getConstraints()
if not IsValid(this) then return self:throw("Invalid entity!", {}) end
if not constraint.HasConstraints(this) then return {} end
local result = getConnectedEntities(this)
table.remove(result,1) -- <this> is always in the first position
self.prf = self.prf + #result * 30
return result
end
--[[
Returns an '''array''' constaining all entities directly or indirectly connected to <this>
supports filtering, see buildFilter above
]]
[nodiscard]
e2function array entity:getConnectedEntities(...filters)
if not IsValid(this) then return self:throw("Invalid entity!", {}) end
local result = getConnectedEntities(this,buildFilter(filters))
self.prf = self.prf + #result * 30
return result
end
e2function array entity:getConnectedEntities(array filters)
if not IsValid(this) then return {} end
local result = getConnectedEntities(this,buildFilter(filters))
self.prf = self.prf + #result * 30
return result
end
__e2setcost(5)
--- Returns the number of constraints on <this>.
e2function number entity:hasConstraints()
if not IsValid(this) then return self:throw("Invalid entity!", 0) end
return #constraint_GetTable(this)
end
--- Returns the number of constraints of type <constraintType> on <this>.
e2function number entity:hasConstraints(string constraintType)
if not IsValid(this) then return 0 end
return #constraint_GetTable(this,buildFilter({constraintType}))
end
--- Returns 1 if <this> is constrained to anything, 0 otherwise.
e2function number entity:isConstrained()
if not IsValid(this) then return self:throw("Invalid entity!", 0) end
if not constraint.HasConstraints(this) then return 0 end
return 1
end
-- Returns con.Ent1 or con.Ent2, whichever is not equivalent to ent. Optionally subscripts con with num beforehand.
local function ent1or2(ent, con, num)
if not con then return NULL end
if num then
con = con[num]
if not con then return NULL end
end
local ent1 = con.Ent1
if ent1 == ent then return con.Ent2 or NULL end
return ent1 or NULL
end
--- Returns the first entity <this> was welded to.
e2function entity entity:isWeldedTo()
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_FindConstraint(this, {Weld = true}))
end
--- Returns the <index>th entity <this> was welded to.
e2function entity entity:isWeldedTo(index)
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_FindConstraints(this, {Weld = true}), math.floor(index))
end
--- Returns the first entity <this> was constrained to.
e2function entity entity:isConstrainedTo()
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_GetTable(this), 1)
end
--- Returns the <index>th entity <this> was constrained to.
e2function entity entity:isConstrainedTo(index)
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_GetTable(this), math.floor(index))
end
--- Returns the first entity <this> was constrained to with the given constraint type <constraintType>.
e2function entity entity:isConstrainedTo(string constraintType)
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_FindConstraint(this, buildFilter({constraintType})))
end
--- Returns the <index>th entity <this> was constrained to with the given constraint type <constraintType>.
e2function entity entity:isConstrainedTo(string constraintType, index)
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
if not constraint.HasConstraints(this) then return NULL end
return ent1or2(this, constraint_FindConstraints(this, buildFilter({constraintType})), math.floor(index))
end
--- Returns the '''entity''' <this> is parented to.
e2function entity entity:parent()
if not IsValid(this) then return self:throw("Invalid entity!", NULL) end
return this:GetParent()
end
local getBone = E2Lib.getBone
--- Returns the '''bone''' <this> is parented to.
e2function bone entity:parentBone()
if not IsValid(this) then return nil end
local ent = this:GetParent()
if not IsValid(ent) then return nil end
local bonenum = this:GetParentPhysNum()
return getBone(ent, bonenum)
end
__e2setcost(20)
--- Returns an '''array''' containing all the children of the entity - that is, every entity whose parent is this entity.
e2function array entity:children()
if not IsValid(this) then return {} end
local keytable = this:GetChildren()
local array = {}
local i = 1
for _, ent in pairs(keytable) do
array[i] = ent
i = i + 1
end
return array
end