-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathundo_buffer.txt
554 lines (426 loc) · 15.6 KB
/
undo_buffer.txt
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
--@name Undo Buffer
--@author legokidlogan
--@server
--@model models/hunter/geometric/hex1x1.mdl
--@include lkl/queue.txt
--@include lkl/perf_check.txt
require( "lkl/queue.txt" )
require( "lkl/perf_check.txt" )
-- For use with reading in animFunc, will be auto-filled for you.
local bufferCount
local bufferProps = {}
local bufferPropBasePoss = {}
local bufferPropBaseAngs = {}
local bufferPropOffsetDirs = {}
local moveBufferProp
local offsetBufferProp
local nudgeBufferProp
local offsetBufferPropRadially
-- CONFIG: -----------------------------------------------------------------
local refreshCommand = "/ubr" -- Chat command for removing the buffer props and respawning them.
local centerMaterial = "model_color"
local centerColor = Color( 255, 255, 255, 255 )
local bufferModel = "models/hunter/geometric/hex1x1.mdl"
local bufferMaterial = "model_color"
local bufferColors = {} -- You can manually define colors in here for each hexagon. If the final count is not 6, expect strange results.
local bufferColorAutoCount = 6 -- number or false. Adds this many colors to bufferColors according to the settings below.
local bufferColorAutoMode = 1 -- 1 = HSV rainbow, 2 = HSV range from-to, 3 = HSV range random
local colorRangeStartHSV = Color( 0, 0.4, 1 ) -- 0-360, 0-1, 0-1
local colorRangeEndHSV = Color( 360, 0.8, 1 ) -- 0-360, 0-1, 0-1
local bufferRadius = 82.2
local bufferMass = 50000
local bufferDoParent = true
local animEnabled = true -- Enable animations.
local animPauseIfEmpty = true -- Temporarily pauses the animation if the buffer is empty.
local animPauseIfNotFull = true -- Temporarily pauses the animation if the buffer isn't full.
local animPauseToggleOnUse = true -- Allows the animation to be toggled by pressing E on the chip.
local animStartPaused = false -- Should the animation start paused?
local animInterval = 0.15 -- Small numbers means the animation is smoother, but more intensive.
local animStartDelay = 5 -- Startup delay for the animation to begin once the chip starts.
local function animShouldPause()
return chip():isPlayerHolding()
end
--[[ Animation Area -----
- To set up an animation, just define a function called animFunc( time, dt )
- time is the total time elapsed since the script started.
- dt is the time since the last call to animFunc.
- You can also define a second function called animFunc2( time, dt ) for additional animations.
- If you use it, I personally would recommend using animFunc for pos/ang animation and animFunc2 for colors.
- Use the provided helper functions (defined further below) to move the buffer props and/or chip around.
- Directly below are some example animations, but I strongly recommend making your own!
--]]
--[[
-- Constant rotation -----
local rotSpeed = 2
local function animFunc( time, dt )
local thetaChange = dt * rotSpeed
local rotAxis = -chip():getUp()
local curAng = chip():getAngles()
local newAng = curAng:rotateAroundAxis( rotAxis, thetaChange )
chip():setAngles( newAng )
end
--]]
--[[
-- Oscillating rotation -----
local rotSpeed = 0.5
local rotScale = 180
local function animFunc( time, dt )
local theta = math.sin( time * rotSpeed ) * rotScale
local rotAxis = -chip():getUp()
-- Convert to vector and back to remove the rotation, regardless of the chip being moved by physgun (this is overkill)
local ang = rotAxis:getAngle()
ang = ang:rotateAroundAxis( ang:getRight(), 90 )
-- Set the new rotation
ang = ang:rotateAroundAxis( rotAxis, theta )
chip():setAngles( ang )
end
--]]
--[[
-- Radial oscillation -----
local oscSpeed = 2
local oscDistMin = 0
local oscDistMax = 50
local function animFunc( time, dt )
local dist = math.remap( math.sin( time * oscSpeed ), -1, 1, oscDistMin, oscDistMax )
offsetBufferPropRadially( -1, dist )
end
--]]
--[[
-- Constant HSV shift -----
local hsvShiftSpeed = 10
local function animFunc2( time, dt )
local hueOffset = time * hsvShiftSpeed
for i = 1, bufferColorAutoCount do
local ent = bufferProps[i]
if isValid( ent ) then
local hue = ( i - 1 ) * ( 360 / bufferColorAutoCount )
local color = Color( hue + hueOffset, 1, 1 ):hsvToRGB()
ent:setColor( color )
end
end
end
--]]
-- DVD Bounce -----
--[[
local bounceBounds = 200
local bounceOriginLocal = Vector( 0, 0, -0.5 )
local hexWidth = 95.4
local hexHeight = 82.686
local hexSpeedMin = 20
local hexSpeedMax = 50
local hexDepthMax = 2
local LOCAL_EDGE_X = Vector( 1, 0, 0 ) * hexHeight / 2
local LOCAL_EDGE_X_MINUS = Vector( -1, 0, 0 ) * hexHeight / 2
local LOCAL_EDGE_Y = Vector( 0, 1, 0 ) * hexWidth / 2
local LOCAL_EDGE_Y_MINUS = Vector( 0, -1, 0 ) * hexWidth / 2
local LOCAL_EDGES = { LOCAL_EDGE_X, LOCAL_EDGE_X_MINUS, LOCAL_EDGE_Y, LOCAL_EDGE_Y_MINUS }
local WALL_LEFT = 1
local WALL_RIGHT = 2
local WALL_UP = 3
local WALL_DOWN = 4
local BOUNCE_BOUNDS_TOO_FAR_SQR = ( bounceBounds * 1.5 ) ^ 2
bufferDoParent = false
local function isInRect( xMin, xMax, yMin, yMax, x, y )
return x >= xMin and x <= xMax and y >= yMin and y <= yMax
end
local function reflectDir( dirX, dirY, wallSide )
if wallSide == WALL_UP or wallSide == WALL_DOWN then
return dirX, -dirY
else
return -dirX, dirY
end
end
local function animFunc( _time, dt )
local bounceOrigin = chip():localToWorld( bounceOriginLocal )
for i = 1, bufferCount do
local hex = bufferProps[i]
if not hex or not hex:isValid() then continue end
local hexPos = chip():worldToLocalVector( hex:getPos() - bounceOrigin )
--local hexPos = chip():worldToLocal( hex:getPos() ) - bounceOriginLocal
local velX = hex._velX or ( ( math.random( 0, 1 ) * 2 - 1 ) * math.rand( hexSpeedMin, hexSpeedMax ) )
local velY = hex._velY or ( ( math.random( 0, 1 ) * 2 - 1 ) * math.rand( hexSpeedMin, hexSpeedMax ) )
local depth = hex._depth or math.rand( -hexDepthMax, 0 )
local posX = hexPos[1]
local posY = hexPos[2]
local velHasChanged = not hex._velX
for wallSide, edge in pairs( LOCAL_EDGES ) do
local edgeX = edge[1]
local edgeY = edge[2]
if not isInRect( -bounceBounds, bounceBounds, -bounceBounds, bounceBounds, posX + edgeX, posY + edgeY ) then
velX, velY = reflectDir( velX, velY, wallSide )
velHasChanged = true
end
end
hex:setPos( chip():localToWorldVector( Vector( posX + velX * dt, posY + velY * dt, depth ) ) + bounceOrigin )
if velHasChanged then
hex._velX = velX
hex._velY = velY
if not hex._depth then
hex._depth = depth
end
if Vector( posX, posY, 0 ):getLengthSqr() > BOUNCE_BOUNDS_TOO_FAR_SQR then
hex:setPos( bounceOrigin )
hex:setAngles( chip():getAngles() )
--hex:remove()
end
end
end
end
--]]
-- END CONFIG -----------------------------------------------------------------
----- ANIMATION HELPERS -----
-- Sets pos/ang relative to chip
-- Index of 0 will move the chip itself
-- Index of -1 will move all buffer props
moveBufferProp = function( ind, pos, ang )
if ind < 0 then
for i = 1, bufferCount do
moveBufferProp( i, pos, ang )
end
return
end
local ent = ind == 0 and chip() or bufferProps[ind]
if not isValid( ent ) then return end
if pos then
-- Parented props automatically setpos in local coords
if not bufferDoParent then
pos = chip():localToWorld( pos )
end
ent:setPos( pos )
end
if ang then
ent:setAngles( chip():localToWorldAngles( ang ) )
end
end
-- Sets pos/ang relative to chip + the prop's base pos/ang
-- Index of 0 will move the chip itself
-- Index of -1 will move all buffer props
offsetBufferProp = function( ind, pos, ang )
if ind == 0 then
moveBufferProp( 0, pos, ang )
elseif ind < 0 then
for i = 1, bufferCount do
offsetBufferProp( i, pos, ang )
end
return
end
local ent = bufferProps[ind]
if not isValid( ent ) then return end
if pos then
pos = pos + bufferPropBasePoss[ind]
end
if ang then
ang = ang + bufferPropBaseAngs[ind]
end
moveBufferProp( ind, pos, ang )
end
-- Sets pos/ang relative to chip + the prop's current pos/ang
-- Index of 0 will move the chip itself
-- Index of -1 will move all buffer props
nudgeBufferProp = function( ind, pos, ang )
if ind < 0 then
for i = 1, bufferCount do
nudgeBufferProp( i, pos, ang )
end
return
end
local ent = ind == 0 and chip() or bufferProps[ind]
if not isValid( ent ) then return end
if pos then
pos = pos + chip():worldToLocal( ent:getPos() )
end
if ang then
ang = ang + chip():worldToLocalAngles( ent:getAngles() )
end
moveBufferProp( ind, pos, ang )
end
-- Sets pos relative to chip + the prop's base pos + radial offset
-- ang behaves the same as in offsetBufferProp()
-- Index of -1 will move all buffer props
offsetBufferPropRadially = function( ind, dist, ang )
if ind == 0 then return end
if ind < 0 then
for i = 1, bufferCount do
offsetBufferPropRadially( i, dist, ang )
end
return
end
local ent = bufferProps[ind]
if not isValid( ent ) then return end
local pos = bufferPropOffsetDirs[ind] * dist
offsetBufferProp( ind, pos, ang )
end
----- SETUP -----
local bufferLookup = {}
local bufferEntries = {}
local bufferQueue
local animIsUsePaused = animPauseToggleOnUse and animStartPaused
if bufferColorAutoCount then
if bufferColorAutoMode == 1 then
for i = 1, bufferColorAutoCount do
local color = Color( ( i - 1 ) * ( 360 / bufferColorAutoCount ), 1, 1 ):hsvToRGB()
table.insert( bufferColors, color )
end
elseif bufferColorAutoMode == 2 then
local startHue = colorRangeStartHSV[1]
local startSaturation = colorRangeStartHSV[2]
local startValue = colorRangeStartHSV[3]
local endHue = colorRangeEndHSV[1]
local endSaturation = colorRangeEndHSV[2]
local endValue = colorRangeEndHSV[3]
for i = 1, bufferColorAutoCount do
local frac = ( i - 1 ) / ( bufferColorAutoCount - 1 )
local color = Color(
math.lerp( frac, startHue, endHue ),
math.lerp( frac, startSaturation, endSaturation ),
math.lerp( frac, startValue, endValue )
):hsvToRGB()
table.insert( bufferColors, color )
end
elseif bufferColorAutoMode == 3 then
local startHue = colorRangeStartHSV[1]
local startSaturation = colorRangeStartHSV[2]
local startValue = colorRangeStartHSV[3]
local endHue = colorRangeEndHSV[1]
local endSaturation = colorRangeEndHSV[2]
local endValue = colorRangeEndHSV[3]
for _ = 1, bufferColorAutoCount do
local color = Color(
math.rand( startHue, endHue ),
math.rand( startSaturation, endSaturation ),
math.rand( startValue, endValue )
):hsvToRGB()
table.insert( bufferColors, color )
end
end
end
bufferCount = #bufferColors
for i, color in pairs( bufferColors ) do
local frac = ( i - 1 ) / bufferCount
local theta = frac * math.pi * 2
local offsetDir = Vector( -math.cos( theta ), math.sin( theta ), 0 )
local pos = offsetDir * bufferRadius
local ang = Angle( 0, 0, 0 )
bufferPropBasePoss[i] = pos
bufferPropBaseAngs[i] = ang
bufferPropOffsetDirs[i] = offsetDir
local entry = {
Index = i,
Pos = pos,
Ang = ang,
Color = color,
}
bufferEntries[i] = entry
end
prop.setPropUndo_lastVal = prop.setPropUndo_lastVal or false
prop._setPropUndo = prop._setPropUndo or prop.setPropUndo
function prop.setPropUndo( bool )
prop.setPropUndo_lastVal = bool
return prop._setPropUndo( bool )
end
local function safeSpawn( pos, ang, model, frozen )
if not prop.canSpawn() then return false end
local ent
local success = pcall( function()
ent = prop.create( pos, ang, model, frozen )
end )
if not success then return false end
return ent
end
hook.add( "Remove", "LKL_UndoBuffer_RemoveProps", function()
for _, ent in pairs( bufferProps ) do
if isValid( ent ) then
ent:remove()
end
end
end )
hook.add( "EntityRemoved", "LKL_UndoBuffer_RespawnProps", function( ent )
if not bufferLookup[ent] then return end
bufferLookup[ent] = nil
table.removeByValue( bufferProps, ent )
local entry = ent.undoBuffer_entry
if not entry then return end
entry.Pos = chip():worldToLocal( ent:getPos() )
entry.Ang = chip():worldToLocalAngles( ent:getAngles() )
entry.Color = ent:getColor()
bufferQueue:addEntryAndStart( entry )
end )
hook.add( "KeyPress", "LKL_UndoBuffer_TogglePause", function( ply, key )
if not animPauseToggleOnUse then return end
if not animEnabled then return end
if ply ~= owner() then return end
if key ~= IN_KEY.USE then return end
local tr = ply:getEyeTrace()
if tr.Entity ~= chip() then return end
if tr.HitPos:getDistanceSqr( tr.StartPos ) > 500^2 then return end
animIsUsePaused = not animIsUsePaused
end )
hook.add( "PlayerSay", "LKL_UndoBuffer_Refresh", function( ply, msg )
if ply ~= owner() then return end
if msg ~= refreshCommand then return end
for _, ent in pairs( bufferProps ) do
if isValid( ent ) then
ent:remove()
end
end
return ""
end )
bufferQueue = Queue:new(
function( _, entry )
local ind = entry.Index
local pos = chip():localToWorld( entry.Pos )
local ang = chip():localToWorldAngles( entry.Ang )
prop._setPropUndo( true )
local ent = safeSpawn( pos, ang, bufferModel, true )
prop._setPropUndo( prop.setPropUndo_lastVal )
if not ent then return true end
ent:setColor( entry.Color )
ent:setMaterial( bufferMaterial )
ent:setMass( bufferMass )
ent:setParent( bufferDoParent and chip() or nil )
ent:setNocollideAll( true )
ent:doNotDuplicate()
ent.undoBuffer_entry = entry
bufferProps[ind] = ent
bufferLookup[ent] = true
end,
nil,
bufferEntries, 0.1, 4, 0.8, "UndoBuffer_SpawnProps"
)
timer.simple( 1, function()
bufferQueue:start()
chip():setMass( bufferMass )
chip():setNocollideAll( true )
chip():setMaterial( centerMaterial )
chip():setColor( centerColor )
end )
if animEnabled then
local useThink = animInterval <= 0
local timeElapsed = 0
local function doAnim()
local count = #bufferProps
if animPauseIfEmpty and count == 0 then return end
if animPauseIfNotFull and count ~= bufferCount then return end
if animIsUsePaused then return end
if animShouldPause() then return end
local prevTime = timeElapsed
if useThink then
timeElapsed = timeElapsed + timer.frametime()
else
timeElapsed = timeElapsed + animInterval
end
if animFunc then
animFunc( timeElapsed, timeElapsed - prevTime )
end
if animFunc2 then
animFunc2( timeElapsed, timeElapsed - prevTime )
end
end
timer.simple( animStartDelay, function()
if useThink then
hook.add( "think", "UndoBuffer_Anim", doAnim )
else
timer.create( "UndoBuffer_Anim", animInterval, 0, doAnim )
end
end )
end