@rbxts/gravity-controller 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3208 @@
1
+ <roblox version="4">
2
+ <Item class="Script" referent="0">
3
+ <Properties>
4
+ <string name="Name">GravityController</string>
5
+ <BinaryString name="AttributesSerialize"></BinaryString>
6
+ <bool name="DefinesCapabilities">false</bool>
7
+ <bool name="Disabled">false</bool>
8
+ <Content name="LinkedSource">
9
+ <null>
10
+ </null>
11
+ </Content>
12
+ <token name="RunContext">0</token>
13
+ <string name="Source">local ReplicatedStorage = game:GetService("ReplicatedStorage")
14
+ local StarterPlayerScripts = game:GetService("StarterPlayer"):WaitForChild("StarterPlayerScripts")
15
+ local StarterCharacterScripts = game:GetService("StarterPlayer"):WaitForChild("StarterCharacterScripts")
16
+
17
+ local Client = script:WaitForChild("Client")
18
+
19
+ local function replace(child, parent)
20
+ local found = parent:FindFirstChild(child.Name)
21
+ if found then found:Destroy() end
22
+ child.Parent = parent
23
+ end
24
+
25
+ replace(Client:WaitForChild("PlayerScriptsLoader"), StarterPlayerScripts)
26
+ replace(Client:WaitForChild("RbxCharacterSounds"), StarterPlayerScripts)
27
+ replace(Client:WaitForChild("Animate"), StarterCharacterScripts)
28
+
29
+ script:WaitForChild("GravityController").Parent = ReplicatedStorage</string>
30
+ <int64 name="SourceAssetId">-1</int64>
31
+ <BinaryString name="Tags"></BinaryString>
32
+ </Properties>
33
+ <Item class="Folder" referent="1">
34
+ <Properties>
35
+ <string name="Name">Client</string>
36
+ <BinaryString name="AttributesSerialize"></BinaryString>
37
+ <bool name="DefinesCapabilities">false</bool>
38
+ <int64 name="SourceAssetId">-1</int64>
39
+ <BinaryString name="Tags"></BinaryString>
40
+ </Properties>
41
+ <Item class="LocalScript" referent="2">
42
+ <Properties>
43
+ <string name="Name">Animate</string>
44
+ <BinaryString name="AttributesSerialize"></BinaryString>
45
+ <bool name="DefinesCapabilities">false</bool>
46
+ <bool name="Disabled">false</bool>
47
+ <Content name="LinkedSource">
48
+ <null>
49
+ </null>
50
+ </Content>
51
+ <token name="RunContext">0</token>
52
+ <string name="Source">require(script:WaitForChild("Controller"))</string>
53
+ <int64 name="SourceAssetId">-1</int64>
54
+ <BinaryString name="Tags"></BinaryString>
55
+ </Properties>
56
+ <Item class="ModuleScript" referent="3">
57
+ <Properties>
58
+ <string name="Name">Controller</string>
59
+ <BinaryString name="AttributesSerialize"></BinaryString>
60
+ <bool name="DefinesCapabilities">false</bool>
61
+ <Content name="LinkedSource">
62
+ <null>
63
+ </null>
64
+ </Content>
65
+ <string name="Source">local animate = script.Parent
66
+ local humanoid = animate.Parent:WaitForChild("Humanoid")
67
+ local loaded = animate:WaitForChild("Loaded")
68
+
69
+ require(animate:WaitForChild("VerifyAnims"))(humanoid, animate)
70
+
71
+ local output
72
+ if humanoid.RigType == Enum.HumanoidRigType.R6 then
73
+ output = require(animate:WaitForChild("R6"))
74
+ else
75
+ output = require(animate:WaitForChild("R15"))
76
+ end
77
+
78
+ loaded.Value = true
79
+
80
+ return output</string>
81
+ <int64 name="SourceAssetId">-1</int64>
82
+ <BinaryString name="Tags"></BinaryString>
83
+ </Properties>
84
+ </Item>
85
+ <Item class="BoolValue" referent="4">
86
+ <Properties>
87
+ <string name="Name">Loaded</string>
88
+ <BinaryString name="AttributesSerialize"></BinaryString>
89
+ <bool name="DefinesCapabilities">false</bool>
90
+ <int64 name="SourceAssetId">-1</int64>
91
+ <BinaryString name="Tags"></BinaryString>
92
+ <bool name="Value">false</bool>
93
+ </Properties>
94
+ </Item>
95
+ <Item class="BindableFunction" referent="5">
96
+ <Properties>
97
+ <string name="Name">PlayEmote</string>
98
+ <BinaryString name="AttributesSerialize"></BinaryString>
99
+ <bool name="DefinesCapabilities">false</bool>
100
+ <int64 name="SourceAssetId">-1</int64>
101
+ <BinaryString name="Tags"></BinaryString>
102
+ </Properties>
103
+ </Item>
104
+ <Item class="ModuleScript" referent="6">
105
+ <Properties>
106
+ <string name="Name">R15</string>
107
+ <BinaryString name="AttributesSerialize"></BinaryString>
108
+ <bool name="DefinesCapabilities">false</bool>
109
+ <Content name="LinkedSource">
110
+ <null>
111
+ </null>
112
+ </Content>
113
+ <string name="Source"><![CDATA[local script = script.Parent
114
+
115
+ local Character = script.Parent
116
+ local Humanoid = Character:WaitForChild("Humanoid")
117
+ local pose = "Standing"
118
+
119
+ local userNoUpdateOnLoopSuccess, userNoUpdateOnLoopValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNoUpdateOnLoop") end)
120
+ local userNoUpdateOnLoop = userNoUpdateOnLoopSuccess and userNoUpdateOnLoopValue
121
+ local userAnimationSpeedDampeningSuccess, userAnimationSpeedDampeningValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserAnimationSpeedDampening") end)
122
+ local userAnimationSpeedDampening = userAnimationSpeedDampeningSuccess and userAnimationSpeedDampeningValue
123
+
124
+ local animateScriptEmoteHookFlagExists, animateScriptEmoteHookFlagEnabled = pcall(function()
125
+ return UserSettings():IsUserFeatureEnabled("UserAnimateScriptEmoteHook")
126
+ end)
127
+ local FFlagAnimateScriptEmoteHook = animateScriptEmoteHookFlagExists and animateScriptEmoteHookFlagEnabled
128
+
129
+ local AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
130
+ local HumanoidHipHeight = 2
131
+
132
+ local EMOTE_TRANSITION_TIME = 0.1
133
+
134
+ local currentAnim = ""
135
+ local currentAnimInstance = nil
136
+ local currentAnimTrack = nil
137
+ local currentAnimKeyframeHandler = nil
138
+ local currentAnimSpeed = 1.0
139
+
140
+ local runAnimTrack = nil
141
+ local runAnimKeyframeHandler = nil
142
+
143
+ local animTable = {}
144
+ local animNames = {
145
+ idle = {
146
+ { id = "http://www.roblox.com/asset/?id=507766666", weight = 1 },
147
+ { id = "http://www.roblox.com/asset/?id=507766951", weight = 1 },
148
+ { id = "http://www.roblox.com/asset/?id=507766388", weight = 9 }
149
+ },
150
+ walk = {
151
+ { id = "http://www.roblox.com/asset/?id=507777826", weight = 10 }
152
+ },
153
+ run = {
154
+ { id = "http://www.roblox.com/asset/?id=507767714", weight = 10 }
155
+ },
156
+ swim = {
157
+ { id = "http://www.roblox.com/asset/?id=507784897", weight = 10 }
158
+ },
159
+ swimidle = {
160
+ { id = "http://www.roblox.com/asset/?id=507785072", weight = 10 }
161
+ },
162
+ jump = {
163
+ { id = "http://www.roblox.com/asset/?id=507765000", weight = 10 }
164
+ },
165
+ fall = {
166
+ { id = "http://www.roblox.com/asset/?id=507767968", weight = 10 }
167
+ },
168
+ climb = {
169
+ { id = "http://www.roblox.com/asset/?id=507765644", weight = 10 }
170
+ },
171
+ sit = {
172
+ { id = "http://www.roblox.com/asset/?id=2506281703", weight = 10 }
173
+ },
174
+ toolnone = {
175
+ { id = "http://www.roblox.com/asset/?id=507768375", weight = 10 }
176
+ },
177
+ toolslash = {
178
+ { id = "http://www.roblox.com/asset/?id=522635514", weight = 10 }
179
+ },
180
+ toollunge = {
181
+ { id = "http://www.roblox.com/asset/?id=522638767", weight = 10 }
182
+ },
183
+ wave = {
184
+ { id = "http://www.roblox.com/asset/?id=507770239", weight = 10 }
185
+ },
186
+ point = {
187
+ { id = "http://www.roblox.com/asset/?id=507770453", weight = 10 }
188
+ },
189
+ dance = {
190
+ { id = "http://www.roblox.com/asset/?id=507771019", weight = 10 },
191
+ { id = "http://www.roblox.com/asset/?id=507771955", weight = 10 },
192
+ { id = "http://www.roblox.com/asset/?id=507772104", weight = 10 }
193
+ },
194
+ dance2 = {
195
+ { id = "http://www.roblox.com/asset/?id=507776043", weight = 10 },
196
+ { id = "http://www.roblox.com/asset/?id=507776720", weight = 10 },
197
+ { id = "http://www.roblox.com/asset/?id=507776879", weight = 10 }
198
+ },
199
+ dance3 = {
200
+ { id = "http://www.roblox.com/asset/?id=507777268", weight = 10 },
201
+ { id = "http://www.roblox.com/asset/?id=507777451", weight = 10 },
202
+ { id = "http://www.roblox.com/asset/?id=507777623", weight = 10 }
203
+ },
204
+ laugh = {
205
+ { id = "http://www.roblox.com/asset/?id=507770818", weight = 10 }
206
+ },
207
+ cheer = {
208
+ { id = "http://www.roblox.com/asset/?id=507770677", weight = 10 }
209
+ },
210
+ }
211
+
212
+ -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
213
+ local emoteNames = { wave = false, point = false, dance = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
214
+
215
+ local PreloadAnimsUserFlag = false
216
+ local PreloadedAnims = {}
217
+ local successPreloadAnim, msgPreloadAnim = pcall(function()
218
+ PreloadAnimsUserFlag = UserSettings():IsUserFeatureEnabled("UserPreloadAnimations")
219
+ end)
220
+ if not successPreloadAnim then
221
+ PreloadAnimsUserFlag = false
222
+ end
223
+
224
+ math.randomseed(tick())
225
+
226
+ function findExistingAnimationInSet(set, anim)
227
+ if set == nil or anim == nil then
228
+ return 0
229
+ end
230
+
231
+ for idx = 1, set.count, 1 do
232
+ if set[idx].anim.AnimationId == anim.AnimationId then
233
+ return idx
234
+ end
235
+ end
236
+
237
+ return 0
238
+ end
239
+
240
+ function configureAnimationSet(name, fileList)
241
+ if (animTable[name] ~= nil) then
242
+ for _, connection in pairs(animTable[name].connections) do
243
+ connection:disconnect()
244
+ end
245
+ end
246
+ animTable[name] = {}
247
+ animTable[name].count = 0
248
+ animTable[name].totalWeight = 0
249
+ animTable[name].connections = {}
250
+
251
+ local allowCustomAnimations = true
252
+
253
+ local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
254
+ if not success then
255
+ allowCustomAnimations = true
256
+ end
257
+
258
+ -- check for config values
259
+ local config = script:FindFirstChild(name)
260
+ if (allowCustomAnimations and config ~= nil) then
261
+ table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
262
+ table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
263
+
264
+ local idx = 0
265
+ for _, childPart in pairs(config:GetChildren()) do
266
+ if (childPart:IsA("Animation")) then
267
+ local newWeight = 1
268
+ local weightObject = childPart:FindFirstChild("Weight")
269
+ if (weightObject ~= nil) then
270
+ newWeight = weightObject.Value
271
+ end
272
+ animTable[name].count = animTable[name].count + 1
273
+ idx = animTable[name].count
274
+ animTable[name][idx] = {}
275
+ animTable[name][idx].anim = childPart
276
+ animTable[name][idx].weight = newWeight
277
+ animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
278
+ table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
279
+ table.insert(animTable[name].connections, childPart.ChildAdded:connect(function(property) configureAnimationSet(name, fileList) end))
280
+ table.insert(animTable[name].connections, childPart.ChildRemoved:connect(function(property) configureAnimationSet(name, fileList) end))
281
+ end
282
+ end
283
+ end
284
+
285
+ -- fallback to defaults
286
+ if (animTable[name].count <= 0) then
287
+ for idx, anim in pairs(fileList) do
288
+ animTable[name][idx] = {}
289
+ animTable[name][idx].anim = Instance.new("Animation")
290
+ animTable[name][idx].anim.Name = name
291
+ animTable[name][idx].anim.AnimationId = anim.id
292
+ animTable[name][idx].weight = anim.weight
293
+ animTable[name].count = animTable[name].count + 1
294
+ animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
295
+ end
296
+ end
297
+
298
+ -- preload anims
299
+ if PreloadAnimsUserFlag then
300
+ for i, animType in pairs(animTable) do
301
+ for idx = 1, animType.count, 1 do
302
+ if PreloadedAnims[animType[idx].anim.AnimationId] == nil then
303
+ Humanoid:LoadAnimation(animType[idx].anim)
304
+ PreloadedAnims[animType[idx].anim.AnimationId] = true
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
310
+
311
+ ------------------------------------------------------------------------------------------------------------
312
+
313
+ function configureAnimationSetOld(name, fileList)
314
+ if (animTable[name] ~= nil) then
315
+ for _, connection in pairs(animTable[name].connections) do
316
+ connection:disconnect()
317
+ end
318
+ end
319
+ animTable[name] = {}
320
+ animTable[name].count = 0
321
+ animTable[name].totalWeight = 0
322
+ animTable[name].connections = {}
323
+
324
+ local allowCustomAnimations = true
325
+
326
+ local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
327
+ if not success then
328
+ allowCustomAnimations = true
329
+ end
330
+
331
+ -- check for config values
332
+ local config = script:FindFirstChild(name)
333
+ if (allowCustomAnimations and config ~= nil) then
334
+ table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
335
+ table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
336
+ local idx = 1
337
+ for _, childPart in pairs(config:GetChildren()) do
338
+ if (childPart:IsA("Animation")) then
339
+ table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
340
+ animTable[name][idx] = {}
341
+ animTable[name][idx].anim = childPart
342
+ local weightObject = childPart:FindFirstChild("Weight")
343
+ if (weightObject == nil) then
344
+ animTable[name][idx].weight = 1
345
+ else
346
+ animTable[name][idx].weight = weightObject.Value
347
+ end
348
+ animTable[name].count = animTable[name].count + 1
349
+ animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
350
+ idx = idx + 1
351
+ end
352
+ end
353
+ end
354
+
355
+ -- fallback to defaults
356
+ if (animTable[name].count <= 0) then
357
+ for idx, anim in pairs(fileList) do
358
+ animTable[name][idx] = {}
359
+ animTable[name][idx].anim = Instance.new("Animation")
360
+ animTable[name][idx].anim.Name = name
361
+ animTable[name][idx].anim.AnimationId = anim.id
362
+ animTable[name][idx].weight = anim.weight
363
+ animTable[name].count = animTable[name].count + 1
364
+ animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
365
+ -- print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
366
+ end
367
+ end
368
+
369
+ -- preload anims
370
+ if PreloadAnimsUserFlag then
371
+ for i, animType in pairs(animTable) do
372
+ for idx = 1, animType.count, 1 do
373
+ Humanoid:LoadAnimation(animType[idx].anim)
374
+ end
375
+ end
376
+ end
377
+ end
378
+
379
+ -- Setup animation objects
380
+ function scriptChildModified(child)
381
+ local fileList = animNames[child.Name]
382
+ if (fileList ~= nil) then
383
+ configureAnimationSet(child.Name, fileList)
384
+ end
385
+ end
386
+
387
+ script.ChildAdded:connect(scriptChildModified)
388
+ script.ChildRemoved:connect(scriptChildModified)
389
+
390
+
391
+ for name, fileList in pairs(animNames) do
392
+ configureAnimationSet(name, fileList)
393
+ end
394
+
395
+ -- ANIMATION
396
+
397
+ -- declarations
398
+ local toolAnim = "None"
399
+ local toolAnimTime = 0
400
+
401
+ local jumpAnimTime = 0
402
+ local jumpAnimDuration = 0.31
403
+
404
+ local toolTransitionTime = 0.1
405
+ local fallTransitionTime = 0.2
406
+
407
+ local currentlyPlayingEmote = false
408
+
409
+ -- functions
410
+
411
+ function stopAllAnimations()
412
+ local oldAnim = currentAnim
413
+
414
+ -- return to idle if finishing an emote
415
+ if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
416
+ oldAnim = "idle"
417
+ end
418
+
419
+ if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
420
+ oldAnim = "idle"
421
+ currentlyPlayingEmote = false
422
+ end
423
+
424
+ currentAnim = ""
425
+ currentAnimInstance = nil
426
+ if (currentAnimKeyframeHandler ~= nil) then
427
+ currentAnimKeyframeHandler:disconnect()
428
+ end
429
+
430
+ if (currentAnimTrack ~= nil) then
431
+ currentAnimTrack:Stop()
432
+ currentAnimTrack:Destroy()
433
+ currentAnimTrack = nil
434
+ end
435
+
436
+ -- clean up walk if there is one
437
+ if (runAnimKeyframeHandler ~= nil) then
438
+ runAnimKeyframeHandler:disconnect()
439
+ end
440
+
441
+ if (runAnimTrack ~= nil) then
442
+ runAnimTrack:Stop()
443
+ runAnimTrack:Destroy()
444
+ runAnimTrack = nil
445
+ end
446
+
447
+ return oldAnim
448
+ end
449
+
450
+ function getHeightScale()
451
+ if Humanoid then
452
+ if not Humanoid.AutomaticScalingEnabled then
453
+ return 1
454
+ end
455
+
456
+ local scale = Humanoid.HipHeight / HumanoidHipHeight
457
+ if userAnimationSpeedDampening then
458
+ if AnimationSpeedDampeningObject == nil then
459
+ AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
460
+ end
461
+ if AnimationSpeedDampeningObject ~= nil then
462
+ scale = 1 + (Humanoid.HipHeight - HumanoidHipHeight) * AnimationSpeedDampeningObject.Value / HumanoidHipHeight
463
+ end
464
+ end
465
+ return scale
466
+ end
467
+ return 1
468
+ end
469
+
470
+ local smallButNotZero = 0.0001
471
+ function setRunSpeed(speed)
472
+ local speedScaled = speed * 1.25
473
+ local heightScale = getHeightScale()
474
+ local runSpeed = speedScaled / heightScale
475
+
476
+ if runSpeed ~= currentAnimSpeed then
477
+ if runSpeed < 0.33 then
478
+ currentAnimTrack:AdjustWeight(1.0)
479
+ runAnimTrack:AdjustWeight(smallButNotZero)
480
+ elseif runSpeed < 0.66 then
481
+ local weight = ((runSpeed - 0.33) / 0.33)
482
+ currentAnimTrack:AdjustWeight(1.0 - weight + smallButNotZero)
483
+ runAnimTrack:AdjustWeight(weight + smallButNotZero)
484
+ else
485
+ currentAnimTrack:AdjustWeight(smallButNotZero)
486
+ runAnimTrack:AdjustWeight(1.0)
487
+ end
488
+ currentAnimSpeed = runSpeed
489
+ runAnimTrack:AdjustSpeed(runSpeed)
490
+ currentAnimTrack:AdjustSpeed(runSpeed)
491
+ end
492
+ end
493
+
494
+ function setAnimationSpeed(speed)
495
+ if currentAnim == "walk" then
496
+ setRunSpeed(speed)
497
+ else
498
+ if speed ~= currentAnimSpeed then
499
+ currentAnimSpeed = speed
500
+ currentAnimTrack:AdjustSpeed(currentAnimSpeed)
501
+ end
502
+ end
503
+ end
504
+
505
+ function keyFrameReachedFunc(frameName)
506
+ if (frameName == "End") then
507
+ if currentAnim == "walk" then
508
+ if userNoUpdateOnLoop == true then
509
+ if runAnimTrack.Looped ~= true then
510
+ runAnimTrack.TimePosition = 0.0
511
+ end
512
+ if currentAnimTrack.Looped ~= true then
513
+ currentAnimTrack.TimePosition = 0.0
514
+ end
515
+ else
516
+ runAnimTrack.TimePosition = 0.0
517
+ currentAnimTrack.TimePosition = 0.0
518
+ end
519
+ else
520
+ local repeatAnim = currentAnim
521
+ -- return to idle if finishing an emote
522
+ if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
523
+ repeatAnim = "idle"
524
+ end
525
+
526
+ if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
527
+ if currentAnimTrack.Looped then
528
+ -- Allow the emote to loop
529
+ return
530
+ end
531
+
532
+ repeatAnim = "idle"
533
+ currentlyPlayingEmote = false
534
+ end
535
+
536
+ local animSpeed = currentAnimSpeed
537
+ playAnimation(repeatAnim, 0.15, Humanoid)
538
+ setAnimationSpeed(animSpeed)
539
+ end
540
+ end
541
+ end
542
+
543
+ function rollAnimation(animName)
544
+ local roll = math.random(1, animTable[animName].totalWeight)
545
+ local origRoll = roll
546
+ local idx = 1
547
+ while (roll > animTable[animName][idx].weight) do
548
+ roll = roll - animTable[animName][idx].weight
549
+ idx = idx + 1
550
+ end
551
+ return idx
552
+ end
553
+
554
+ local function switchToAnim(anim, animName, transitionTime, humanoid)
555
+ -- switch animation
556
+ if (anim ~= currentAnimInstance) then
557
+
558
+ if (currentAnimTrack ~= nil) then
559
+ currentAnimTrack:Stop(transitionTime)
560
+ currentAnimTrack:Destroy()
561
+ end
562
+
563
+ if (runAnimTrack ~= nil) then
564
+ runAnimTrack:Stop(transitionTime)
565
+ runAnimTrack:Destroy()
566
+ if userNoUpdateOnLoop == true then
567
+ runAnimTrack = nil
568
+ end
569
+ end
570
+
571
+ currentAnimSpeed = 1.0
572
+
573
+ -- load it to the humanoid; get AnimationTrack
574
+ currentAnimTrack = humanoid:LoadAnimation(anim)
575
+ currentAnimTrack.Priority = Enum.AnimationPriority.Core
576
+
577
+ -- play the animation
578
+ currentAnimTrack:Play(transitionTime)
579
+ currentAnim = animName
580
+ currentAnimInstance = anim
581
+
582
+ -- set up keyframe name triggers
583
+ if (currentAnimKeyframeHandler ~= nil) then
584
+ currentAnimKeyframeHandler:disconnect()
585
+ end
586
+ currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
587
+
588
+ -- check to see if we need to blend a walk/run animation
589
+ if animName == "walk" then
590
+ local runAnimName = "run"
591
+ local runIdx = rollAnimation(runAnimName)
592
+
593
+ runAnimTrack = humanoid:LoadAnimation(animTable[runAnimName][runIdx].anim)
594
+ runAnimTrack.Priority = Enum.AnimationPriority.Core
595
+ runAnimTrack:Play(transitionTime)
596
+
597
+ if (runAnimKeyframeHandler ~= nil) then
598
+ runAnimKeyframeHandler:disconnect()
599
+ end
600
+ runAnimKeyframeHandler = runAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
601
+ end
602
+ end
603
+ end
604
+
605
+ function playAnimation(animName, transitionTime, humanoid)
606
+ local idx = rollAnimation(animName)
607
+ local anim = animTable[animName][idx].anim
608
+
609
+ switchToAnim(anim, animName, transitionTime, humanoid)
610
+ currentlyPlayingEmote = false
611
+ end
612
+
613
+ function playEmote(emoteAnim, transitionTime, humanoid)
614
+ switchToAnim(emoteAnim, emoteAnim.Name, transitionTime, humanoid)
615
+ currentlyPlayingEmote = true
616
+ end
617
+
618
+ -------------------------------------------------------------------------------------------
619
+ -------------------------------------------------------------------------------------------
620
+
621
+ local toolAnimName = ""
622
+ local toolAnimTrack = nil
623
+ local toolAnimInstance = nil
624
+ local currentToolAnimKeyframeHandler = nil
625
+
626
+ function toolKeyFrameReachedFunc(frameName)
627
+ if (frameName == "End") then
628
+ playToolAnimation(toolAnimName, 0.0, Humanoid)
629
+ end
630
+ end
631
+
632
+
633
+ function playToolAnimation(animName, transitionTime, humanoid, priority)
634
+ local idx = rollAnimation(animName)
635
+ local anim = animTable[animName][idx].anim
636
+
637
+ if (toolAnimInstance ~= anim) then
638
+
639
+ if (toolAnimTrack ~= nil) then
640
+ toolAnimTrack:Stop()
641
+ toolAnimTrack:Destroy()
642
+ transitionTime = 0
643
+ end
644
+
645
+ -- load it to the humanoid; get AnimationTrack
646
+ toolAnimTrack = humanoid:LoadAnimation(anim)
647
+ if priority then
648
+ toolAnimTrack.Priority = priority
649
+ end
650
+
651
+ -- play the animation
652
+ toolAnimTrack:Play(transitionTime)
653
+ toolAnimName = animName
654
+ toolAnimInstance = anim
655
+
656
+ currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
657
+ end
658
+ end
659
+
660
+ function stopToolAnimations()
661
+ local oldAnim = toolAnimName
662
+
663
+ if (currentToolAnimKeyframeHandler ~= nil) then
664
+ currentToolAnimKeyframeHandler:disconnect()
665
+ end
666
+
667
+ toolAnimName = ""
668
+ toolAnimInstance = nil
669
+ if (toolAnimTrack ~= nil) then
670
+ toolAnimTrack:Stop()
671
+ toolAnimTrack:Destroy()
672
+ toolAnimTrack = nil
673
+ end
674
+
675
+ return oldAnim
676
+ end
677
+
678
+ -------------------------------------------------------------------------------------------
679
+ -------------------------------------------------------------------------------------------
680
+ -- STATE CHANGE HANDLERS
681
+
682
+ function onRunning(speed)
683
+ if speed > 0.75 then
684
+ local scale = 16.0
685
+ playAnimation("walk", 0.2, Humanoid)
686
+ setAnimationSpeed(speed / scale)
687
+ pose = "Running"
688
+ else
689
+ if emoteNames[currentAnim] == nil and not currentlyPlayingEmote then
690
+ playAnimation("idle", 0.2, Humanoid)
691
+ pose = "Standing"
692
+ end
693
+ end
694
+ end
695
+
696
+ function onDied()
697
+ pose = "Dead"
698
+ end
699
+
700
+ function onJumping()
701
+ playAnimation("jump", 0.1, Humanoid)
702
+ jumpAnimTime = jumpAnimDuration
703
+ pose = "Jumping"
704
+ end
705
+
706
+ function onClimbing(speed)
707
+ local scale = 5.0
708
+ playAnimation("climb", 0.1, Humanoid)
709
+ setAnimationSpeed(speed / scale)
710
+ pose = "Climbing"
711
+ end
712
+
713
+ function onGettingUp()
714
+ pose = "GettingUp"
715
+ end
716
+
717
+ function onFreeFall()
718
+ if (jumpAnimTime <= 0) then
719
+ playAnimation("fall", fallTransitionTime, Humanoid)
720
+ end
721
+ pose = "FreeFall"
722
+ end
723
+
724
+ function onFallingDown()
725
+ pose = "FallingDown"
726
+ end
727
+
728
+ function onSeated()
729
+ pose = "Seated"
730
+ end
731
+
732
+ function onPlatformStanding()
733
+ pose = "PlatformStanding"
734
+ end
735
+
736
+ -------------------------------------------------------------------------------------------
737
+ -------------------------------------------------------------------------------------------
738
+
739
+ function onSwimming(speed)
740
+ if speed > 1.00 then
741
+ local scale = 10.0
742
+ playAnimation("swim", 0.4, Humanoid)
743
+ setAnimationSpeed(speed / scale)
744
+ pose = "Swimming"
745
+ else
746
+ playAnimation("swimidle", 0.4, Humanoid)
747
+ pose = "Standing"
748
+ end
749
+ end
750
+
751
+ function animateTool()
752
+ if (toolAnim == "None") then
753
+ playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
754
+ return
755
+ end
756
+
757
+ if (toolAnim == "Slash") then
758
+ playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
759
+ return
760
+ end
761
+
762
+ if (toolAnim == "Lunge") then
763
+ playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
764
+ return
765
+ end
766
+ end
767
+
768
+ function getToolAnim(tool)
769
+ for _, c in ipairs(tool:GetChildren()) do
770
+ if c.Name == "toolanim" and c.className == "StringValue" then
771
+ return c
772
+ end
773
+ end
774
+ return nil
775
+ end
776
+
777
+ local lastTick = 0
778
+
779
+ function stepAnimate(currentTime)
780
+ local amplitude = 1
781
+ local frequency = 1
782
+ local deltaTime = currentTime - lastTick
783
+ lastTick = currentTime
784
+
785
+ local climbFudge = 0
786
+ local setAngles = false
787
+
788
+ if (jumpAnimTime > 0) then
789
+ jumpAnimTime = jumpAnimTime - deltaTime
790
+ end
791
+
792
+ if (pose == "FreeFall" and jumpAnimTime <= 0) then
793
+ playAnimation("fall", fallTransitionTime, Humanoid)
794
+ elseif (pose == "Seated") then
795
+ playAnimation("sit", 0.5, Humanoid)
796
+ return
797
+ elseif (pose == "Running") then
798
+ playAnimation("walk", 0.2, Humanoid)
799
+ elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
800
+ stopAllAnimations()
801
+ amplitude = 0.1
802
+ frequency = 1
803
+ setAngles = true
804
+ end
805
+
806
+ -- Tool Animation handling
807
+ local tool = Character:FindFirstChildOfClass("Tool")
808
+ if tool and tool:FindFirstChild("Handle") then
809
+ local animStringValueObject = getToolAnim(tool)
810
+
811
+ if animStringValueObject then
812
+ toolAnim = animStringValueObject.Value
813
+ -- message recieved, delete StringValue
814
+ animStringValueObject.Parent = nil
815
+ toolAnimTime = currentTime + .3
816
+ end
817
+
818
+ if currentTime > toolAnimTime then
819
+ toolAnimTime = 0
820
+ toolAnim = "None"
821
+ end
822
+
823
+ animateTool()
824
+ else
825
+ stopToolAnimations()
826
+ toolAnim = "None"
827
+ toolAnimInstance = nil
828
+ toolAnimTime = 0
829
+ end
830
+ end
831
+
832
+ -- connect events
833
+
834
+ local events = {}
835
+ local eventHum = Humanoid
836
+ local RepHumanoid = script:WaitForChild("ReplicatedHumanoid")
837
+
838
+ local function onUnhook()
839
+ for i = 1, #events do
840
+ events[i]:Disconnect()
841
+ end
842
+ events = {}
843
+ end
844
+
845
+ local function onHook()
846
+ onUnhook()
847
+
848
+ pose = Humanoid.Sit and "Seated" or "Standing"
849
+
850
+ events = {
851
+ eventHum.Died:connect(onDied),
852
+ eventHum.Running:connect(onRunning),
853
+ eventHum.Jumping:connect(onJumping),
854
+ eventHum.Climbing:connect(onClimbing),
855
+ eventHum.GettingUp:connect(onGettingUp),
856
+ eventHum.FreeFalling:connect(onFreeFall),
857
+ eventHum.FallingDown:connect(onFallingDown),
858
+ eventHum.Seated:connect(onSeated),
859
+ eventHum.PlatformStanding:connect(onPlatformStanding),
860
+ eventHum.Swimming:connect(onSwimming)
861
+ }
862
+ end
863
+
864
+ RepHumanoid.Changed:Connect(function()
865
+ eventHum = RepHumanoid.Value
866
+ onHook()
867
+ end)
868
+
869
+ RepHumanoid.Value = Humanoid
870
+
871
+ -- setup emote chat hook
872
+ game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
873
+ local emote = ""
874
+ if (string.sub(msg, 1, 3) == "/e ") then
875
+ emote = string.sub(msg, 4)
876
+ elseif (string.sub(msg, 1, 7) == "/emote ") then
877
+ emote = string.sub(msg, 8)
878
+ end
879
+
880
+ if (pose == "Standing" and emoteNames[emote] ~= nil) then
881
+ playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
882
+ end
883
+ end)
884
+
885
+ -- emote bindable hook
886
+ if FFlagAnimateScriptEmoteHook then
887
+ script:WaitForChild("PlayEmote").OnInvoke = function(emote)
888
+ -- Only play emotes when idling
889
+ if pose ~= "Standing" then
890
+ return
891
+ end
892
+
893
+ if emoteNames[emote] ~= nil then
894
+ -- Default emotes
895
+ playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
896
+
897
+ return true
898
+ elseif typeof(emote) == "Instance" and emote:IsA("Animation") then
899
+ -- Non-default emotes
900
+ playEmote(emote, EMOTE_TRANSITION_TIME, Humanoid)
901
+ return true
902
+ end
903
+
904
+ -- Return false to indicate that the emote could not be played
905
+ return false
906
+ end
907
+ end
908
+
909
+ -- initialize to idle
910
+ playAnimation("idle", 0.1, Humanoid)
911
+ pose = "Standing"
912
+
913
+ -- loop to handle timed state transitions and tool animations
914
+ spawn(function()
915
+ while Character.Parent ~= nil do
916
+ local _, currentGameTime = wait(0.1)
917
+ stepAnimate(currentGameTime)
918
+ end
919
+ end)
920
+
921
+ return {
922
+ onRunning = onRunning,
923
+ onDied = onDied,
924
+ onJumping = onJumping,
925
+ onClimbing = onClimbing,
926
+ onGettingUp = onGettingUp,
927
+ onFreeFall = onFreeFall,
928
+ onFallingDown = onFallingDown,
929
+ onSeated = onSeated,
930
+ onPlatformStanding = onPlatformStanding,
931
+ onHook = onHook,
932
+ onUnhook = onUnhook
933
+ }
934
+
935
+ ]]></string>
936
+ <int64 name="SourceAssetId">-1</int64>
937
+ <BinaryString name="Tags"></BinaryString>
938
+ </Properties>
939
+ </Item>
940
+ <Item class="ModuleScript" referent="7">
941
+ <Properties>
942
+ <string name="Name">R6</string>
943
+ <BinaryString name="AttributesSerialize"></BinaryString>
944
+ <bool name="DefinesCapabilities">false</bool>
945
+ <Content name="LinkedSource">
946
+ <null>
947
+ </null>
948
+ </Content>
949
+ <string name="Source">local script = script.Parent
950
+
951
+ local Figure = script.Parent
952
+ local Torso = Figure:WaitForChild("Torso")
953
+ local RightShoulder = Torso:WaitForChild("Right Shoulder")
954
+ local LeftShoulder = Torso:WaitForChild("Left Shoulder")
955
+ local RightHip = Torso:WaitForChild("Right Hip")
956
+ local LeftHip = Torso:WaitForChild("Left Hip")
957
+ local Neck = Torso:WaitForChild("Neck")
958
+ local Humanoid = Figure:WaitForChild("Humanoid")
959
+ local pose = "Standing"
960
+
961
+ local currentAnim = ""
962
+ local currentAnimInstance = nil
963
+ local currentAnimTrack = nil
964
+ local currentAnimKeyframeHandler = nil
965
+ local currentAnimSpeed = 1.0
966
+ local animTable = {}
967
+ local animNames = {
968
+ idle = {
969
+ { id = "http://www.roblox.com/asset/?id=180435571", weight = 9 },
970
+ { id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
971
+ },
972
+ walk = {
973
+ { id = "http://www.roblox.com/asset/?id=180426354", weight = 10 }
974
+ },
975
+ run = {
976
+ { id = "run.xml", weight = 10 }
977
+ },
978
+ jump = {
979
+ { id = "http://www.roblox.com/asset/?id=125750702", weight = 10 }
980
+ },
981
+ fall = {
982
+ { id = "http://www.roblox.com/asset/?id=180436148", weight = 10 }
983
+ },
984
+ climb = {
985
+ { id = "http://www.roblox.com/asset/?id=180436334", weight = 10 }
986
+ },
987
+ sit = {
988
+ { id = "http://www.roblox.com/asset/?id=178130996", weight = 10 }
989
+ },
990
+ toolnone = {
991
+ { id = "http://www.roblox.com/asset/?id=182393478", weight = 10 }
992
+ },
993
+ toolslash = {
994
+ { id = "http://www.roblox.com/asset/?id=129967390", weight = 10 }
995
+ -- { id = "slash.xml", weight = 10 }
996
+ },
997
+ toollunge = {
998
+ { id = "http://www.roblox.com/asset/?id=129967478", weight = 10 }
999
+ },
1000
+ wave = {
1001
+ { id = "http://www.roblox.com/asset/?id=128777973", weight = 10 }
1002
+ },
1003
+ point = {
1004
+ { id = "http://www.roblox.com/asset/?id=128853357", weight = 10 }
1005
+ },
1006
+ dance1 = {
1007
+ { id = "http://www.roblox.com/asset/?id=182435998", weight = 10 },
1008
+ { id = "http://www.roblox.com/asset/?id=182491037", weight = 10 },
1009
+ { id = "http://www.roblox.com/asset/?id=182491065", weight = 10 }
1010
+ },
1011
+ dance2 = {
1012
+ { id = "http://www.roblox.com/asset/?id=182436842", weight = 10 },
1013
+ { id = "http://www.roblox.com/asset/?id=182491248", weight = 10 },
1014
+ { id = "http://www.roblox.com/asset/?id=182491277", weight = 10 }
1015
+ },
1016
+ dance3 = {
1017
+ { id = "http://www.roblox.com/asset/?id=182436935", weight = 10 },
1018
+ { id = "http://www.roblox.com/asset/?id=182491368", weight = 10 },
1019
+ { id = "http://www.roblox.com/asset/?id=182491423", weight = 10 }
1020
+ },
1021
+ laugh = {
1022
+ { id = "http://www.roblox.com/asset/?id=129423131", weight = 10 }
1023
+ },
1024
+ cheer = {
1025
+ { id = "http://www.roblox.com/asset/?id=129423030", weight = 10 }
1026
+ },
1027
+ }
1028
+ local dances = {"dance1", "dance2", "dance3"}
1029
+
1030
+ -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
1031
+ local emoteNames = { wave = false, point = false, dance1 = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
1032
+
1033
+ function configureAnimationSet(name, fileList)
1034
+ if (animTable[name] ~= nil) then
1035
+ for _, connection in pairs(animTable[name].connections) do
1036
+ connection:disconnect()
1037
+ end
1038
+ end
1039
+ animTable[name] = {}
1040
+ animTable[name].count = 0
1041
+ animTable[name].totalWeight = 0
1042
+ animTable[name].connections = {}
1043
+
1044
+ -- check for config values
1045
+ local config = script:FindFirstChild(name)
1046
+ if (config ~= nil) then
1047
+ -- print("Loading anims " .. name)
1048
+ table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
1049
+ table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
1050
+ local idx = 1
1051
+ for _, childPart in pairs(config:GetChildren()) do
1052
+ if (childPart:IsA("Animation")) then
1053
+ table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
1054
+ animTable[name][idx] = {}
1055
+ animTable[name][idx].anim = childPart
1056
+ local weightObject = childPart:FindFirstChild("Weight")
1057
+ if (weightObject == nil) then
1058
+ animTable[name][idx].weight = 1
1059
+ else
1060
+ animTable[name][idx].weight = weightObject.Value
1061
+ end
1062
+ animTable[name].count = animTable[name].count + 1
1063
+ animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
1064
+ -- print(name .. " [" .. idx .. "] " .. animTable[name][idx].anim.AnimationId .. " (" .. animTable[name][idx].weight .. ")")
1065
+ idx = idx + 1
1066
+ end
1067
+ end
1068
+ end
1069
+
1070
+ -- fallback to defaults
1071
+ if (animTable[name].count &lt;= 0) then
1072
+ for idx, anim in pairs(fileList) do
1073
+ animTable[name][idx] = {}
1074
+ animTable[name][idx].anim = Instance.new("Animation")
1075
+ animTable[name][idx].anim.Name = name
1076
+ animTable[name][idx].anim.AnimationId = anim.id
1077
+ animTable[name][idx].weight = anim.weight
1078
+ animTable[name].count = animTable[name].count + 1
1079
+ animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
1080
+ -- print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
1081
+ end
1082
+ end
1083
+ end
1084
+
1085
+ -- Setup animation objects
1086
+ function scriptChildModified(child)
1087
+ local fileList = animNames[child.Name]
1088
+ if (fileList ~= nil) then
1089
+ configureAnimationSet(child.Name, fileList)
1090
+ end
1091
+ end
1092
+
1093
+ script.ChildAdded:connect(scriptChildModified)
1094
+ script.ChildRemoved:connect(scriptChildModified)
1095
+
1096
+
1097
+ for name, fileList in pairs(animNames) do
1098
+ configureAnimationSet(name, fileList)
1099
+ end
1100
+
1101
+ -- ANIMATION
1102
+
1103
+ -- declarations
1104
+ local toolAnim = "None"
1105
+ local toolAnimTime = 0
1106
+
1107
+ local jumpAnimTime = 0
1108
+ local jumpAnimDuration = 0.3
1109
+
1110
+ local toolTransitionTime = 0.1
1111
+ local fallTransitionTime = 0.3
1112
+ local jumpMaxLimbVelocity = 0.75
1113
+
1114
+ -- functions
1115
+
1116
+ function stopAllAnimations()
1117
+ local oldAnim = currentAnim
1118
+
1119
+ -- return to idle if finishing an emote
1120
+ if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
1121
+ oldAnim = "idle"
1122
+ end
1123
+
1124
+ currentAnim = ""
1125
+ currentAnimInstance = nil
1126
+ if (currentAnimKeyframeHandler ~= nil) then
1127
+ currentAnimKeyframeHandler:disconnect()
1128
+ end
1129
+
1130
+ if (currentAnimTrack ~= nil) then
1131
+ currentAnimTrack:Stop()
1132
+ currentAnimTrack:Destroy()
1133
+ currentAnimTrack = nil
1134
+ end
1135
+ return oldAnim
1136
+ end
1137
+
1138
+ function setAnimationSpeed(speed)
1139
+ if speed ~= currentAnimSpeed then
1140
+ currentAnimSpeed = speed
1141
+ currentAnimTrack:AdjustSpeed(currentAnimSpeed)
1142
+ end
1143
+ end
1144
+
1145
+ function keyFrameReachedFunc(frameName)
1146
+ if (frameName == "End") then
1147
+
1148
+ local repeatAnim = currentAnim
1149
+ -- return to idle if finishing an emote
1150
+ if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
1151
+ repeatAnim = "idle"
1152
+ end
1153
+
1154
+ local animSpeed = currentAnimSpeed
1155
+ playAnimation(repeatAnim, 0.0, Humanoid)
1156
+ setAnimationSpeed(animSpeed)
1157
+ end
1158
+ end
1159
+
1160
+ -- Preload animations
1161
+ function playAnimation(animName, transitionTime, humanoid)
1162
+
1163
+ local roll = math.random(1, animTable[animName].totalWeight)
1164
+ local origRoll = roll
1165
+ local idx = 1
1166
+ while (roll > animTable[animName][idx].weight) do
1167
+ roll = roll - animTable[animName][idx].weight
1168
+ idx = idx + 1
1169
+ end
1170
+ -- print(animName .. " " .. idx .. " [" .. origRoll .. "]")
1171
+ local anim = animTable[animName][idx].anim
1172
+
1173
+ -- switch animation
1174
+ if (anim ~= currentAnimInstance) then
1175
+
1176
+ if (currentAnimTrack ~= nil) then
1177
+ currentAnimTrack:Stop(transitionTime)
1178
+ currentAnimTrack:Destroy()
1179
+ end
1180
+
1181
+ currentAnimSpeed = 1.0
1182
+
1183
+ -- load it to the humanoid; get AnimationTrack
1184
+ currentAnimTrack = humanoid:LoadAnimation(anim)
1185
+ currentAnimTrack.Priority = Enum.AnimationPriority.Core
1186
+
1187
+ -- play the animation
1188
+ currentAnimTrack:Play(transitionTime)
1189
+ currentAnim = animName
1190
+ currentAnimInstance = anim
1191
+
1192
+ -- set up keyframe name triggers
1193
+ if (currentAnimKeyframeHandler ~= nil) then
1194
+ currentAnimKeyframeHandler:disconnect()
1195
+ end
1196
+ currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
1197
+
1198
+ end
1199
+
1200
+ end
1201
+
1202
+ -------------------------------------------------------------------------------------------
1203
+ -------------------------------------------------------------------------------------------
1204
+
1205
+ local toolAnimName = ""
1206
+ local toolAnimTrack = nil
1207
+ local toolAnimInstance = nil
1208
+ local currentToolAnimKeyframeHandler = nil
1209
+
1210
+ function toolKeyFrameReachedFunc(frameName)
1211
+ if (frameName == "End") then
1212
+ -- print("Keyframe : ".. frameName)
1213
+ playToolAnimation(toolAnimName, 0.0, Humanoid)
1214
+ end
1215
+ end
1216
+
1217
+
1218
+ function playToolAnimation(animName, transitionTime, humanoid, priority)
1219
+
1220
+ local roll = math.random(1, animTable[animName].totalWeight)
1221
+ local origRoll = roll
1222
+ local idx = 1
1223
+ while (roll > animTable[animName][idx].weight) do
1224
+ roll = roll - animTable[animName][idx].weight
1225
+ idx = idx + 1
1226
+ end
1227
+ -- print(animName .. " * " .. idx .. " [" .. origRoll .. "]")
1228
+ local anim = animTable[animName][idx].anim
1229
+
1230
+ if (toolAnimInstance ~= anim) then
1231
+
1232
+ if (toolAnimTrack ~= nil) then
1233
+ toolAnimTrack:Stop()
1234
+ toolAnimTrack:Destroy()
1235
+ transitionTime = 0
1236
+ end
1237
+
1238
+ -- load it to the humanoid; get AnimationTrack
1239
+ toolAnimTrack = humanoid:LoadAnimation(anim)
1240
+ if priority then
1241
+ toolAnimTrack.Priority = priority
1242
+ end
1243
+
1244
+ -- play the animation
1245
+ toolAnimTrack:Play(transitionTime)
1246
+ toolAnimName = animName
1247
+ toolAnimInstance = anim
1248
+
1249
+ currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
1250
+ end
1251
+ end
1252
+
1253
+ function stopToolAnimations()
1254
+ local oldAnim = toolAnimName
1255
+
1256
+ if (currentToolAnimKeyframeHandler ~= nil) then
1257
+ currentToolAnimKeyframeHandler:disconnect()
1258
+ end
1259
+
1260
+ toolAnimName = ""
1261
+ toolAnimInstance = nil
1262
+ if (toolAnimTrack ~= nil) then
1263
+ toolAnimTrack:Stop()
1264
+ toolAnimTrack:Destroy()
1265
+ toolAnimTrack = nil
1266
+ end
1267
+
1268
+
1269
+ return oldAnim
1270
+ end
1271
+
1272
+ -------------------------------------------------------------------------------------------
1273
+ -------------------------------------------------------------------------------------------
1274
+
1275
+
1276
+ function onRunning(speed)
1277
+ if speed > 0.01 then
1278
+ playAnimation("walk", 0.1, Humanoid)
1279
+ if currentAnimInstance and currentAnimInstance.AnimationId == "http://www.roblox.com/asset/?id=180426354" then
1280
+ setAnimationSpeed(speed / 14.5)
1281
+ end
1282
+ pose = "Running"
1283
+ else
1284
+ if emoteNames[currentAnim] == nil then
1285
+ playAnimation("idle", 0.1, Humanoid)
1286
+ pose = "Standing"
1287
+ end
1288
+ end
1289
+ end
1290
+
1291
+ function onDied()
1292
+ pose = "Dead"
1293
+ end
1294
+
1295
+ function onJumping()
1296
+ playAnimation("jump", 0.1, Humanoid)
1297
+ jumpAnimTime = jumpAnimDuration
1298
+ pose = "Jumping"
1299
+ end
1300
+
1301
+ function onClimbing(speed)
1302
+ playAnimation("climb", 0.1, Humanoid)
1303
+ setAnimationSpeed(speed / 12.0)
1304
+ pose = "Climbing"
1305
+ end
1306
+
1307
+ function onGettingUp()
1308
+ pose = "GettingUp"
1309
+ end
1310
+
1311
+ function onFreeFall()
1312
+ if (jumpAnimTime &lt;= 0) then
1313
+ playAnimation("fall", fallTransitionTime, Humanoid)
1314
+ end
1315
+ pose = "FreeFall"
1316
+ end
1317
+
1318
+ function onFallingDown()
1319
+ pose = "FallingDown"
1320
+ end
1321
+
1322
+ function onSeated()
1323
+ pose = "Seated"
1324
+ end
1325
+
1326
+ function onPlatformStanding()
1327
+ pose = "PlatformStanding"
1328
+ end
1329
+
1330
+ function onSwimming(speed)
1331
+ if speed > 0 then
1332
+ pose = "Running"
1333
+ else
1334
+ pose = "Standing"
1335
+ end
1336
+ end
1337
+
1338
+ function getTool()
1339
+ for _, kid in ipairs(Figure:GetChildren()) do
1340
+ if kid.className == "Tool" then return kid end
1341
+ end
1342
+ return nil
1343
+ end
1344
+
1345
+ function getToolAnim(tool)
1346
+ for _, c in ipairs(tool:GetChildren()) do
1347
+ if c.Name == "toolanim" and c.className == "StringValue" then
1348
+ return c
1349
+ end
1350
+ end
1351
+ return nil
1352
+ end
1353
+
1354
+ function animateTool()
1355
+
1356
+ if (toolAnim == "None") then
1357
+ playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
1358
+ return
1359
+ end
1360
+
1361
+ if (toolAnim == "Slash") then
1362
+ playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
1363
+ return
1364
+ end
1365
+
1366
+ if (toolAnim == "Lunge") then
1367
+ playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
1368
+ return
1369
+ end
1370
+ end
1371
+
1372
+ function moveSit()
1373
+ RightShoulder.MaxVelocity = 0.15
1374
+ LeftShoulder.MaxVelocity = 0.15
1375
+ RightShoulder:SetDesiredAngle(3.14 /2)
1376
+ LeftShoulder:SetDesiredAngle(-3.14 /2)
1377
+ RightHip:SetDesiredAngle(3.14 /2)
1378
+ LeftHip:SetDesiredAngle(-3.14 /2)
1379
+ end
1380
+
1381
+ local lastTick = 0
1382
+
1383
+ function move(time)
1384
+ local amplitude = 1
1385
+ local frequency = 1
1386
+ local deltaTime = time - lastTick
1387
+ lastTick = time
1388
+
1389
+ local climbFudge = 0
1390
+ local setAngles = false
1391
+
1392
+ if (jumpAnimTime > 0) then
1393
+ jumpAnimTime = jumpAnimTime - deltaTime
1394
+ end
1395
+
1396
+ if (pose == "FreeFall" and jumpAnimTime &lt;= 0) then
1397
+ playAnimation("fall", fallTransitionTime, Humanoid)
1398
+ elseif (pose == "Seated") then
1399
+ playAnimation("sit", 0.5, Humanoid)
1400
+ return
1401
+ elseif (pose == "Running") then
1402
+ playAnimation("walk", 0.1, Humanoid)
1403
+ elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
1404
+ -- print("Wha " .. pose)
1405
+ stopAllAnimations()
1406
+ amplitude = 0.1
1407
+ frequency = 1
1408
+ setAngles = true
1409
+ end
1410
+
1411
+ if (setAngles) then
1412
+ local desiredAngle = amplitude * math.sin(time * frequency)
1413
+
1414
+ RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
1415
+ LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
1416
+ RightHip:SetDesiredAngle(-desiredAngle)
1417
+ LeftHip:SetDesiredAngle(-desiredAngle)
1418
+ end
1419
+
1420
+ -- Tool Animation handling
1421
+ local tool = getTool()
1422
+ if tool and tool:FindFirstChild("Handle") then
1423
+
1424
+ local animStringValueObject = getToolAnim(tool)
1425
+
1426
+ if animStringValueObject then
1427
+ toolAnim = animStringValueObject.Value
1428
+ -- message recieved, delete StringValue
1429
+ animStringValueObject.Parent = nil
1430
+ toolAnimTime = time + .3
1431
+ end
1432
+
1433
+ if time > toolAnimTime then
1434
+ toolAnimTime = 0
1435
+ toolAnim = "None"
1436
+ end
1437
+
1438
+ animateTool()
1439
+ else
1440
+ stopToolAnimations()
1441
+ toolAnim = "None"
1442
+ toolAnimInstance = nil
1443
+ toolAnimTime = 0
1444
+ end
1445
+ end
1446
+
1447
+
1448
+ local events = {}
1449
+ local eventHum = Humanoid
1450
+ local RepHumanoid = script:WaitForChild("ReplicatedHumanoid")
1451
+
1452
+ local function onUnhook()
1453
+ for i = 1, #events do
1454
+ events[i]:Disconnect()
1455
+ end
1456
+ events = {}
1457
+ end
1458
+
1459
+ local function onHook()
1460
+ onUnhook()
1461
+
1462
+ pose = Humanoid.Sit and "Seated" or "Standing"
1463
+
1464
+ events = {
1465
+ eventHum.Died:connect(onDied),
1466
+ eventHum.Running:connect(onRunning),
1467
+ eventHum.Jumping:connect(onJumping),
1468
+ eventHum.Climbing:connect(onClimbing),
1469
+ eventHum.GettingUp:connect(onGettingUp),
1470
+ eventHum.FreeFalling:connect(onFreeFall),
1471
+ eventHum.FallingDown:connect(onFallingDown),
1472
+ eventHum.Seated:connect(onSeated),
1473
+ eventHum.PlatformStanding:connect(onPlatformStanding),
1474
+ eventHum.Swimming:connect(onSwimming)
1475
+ }
1476
+ end
1477
+
1478
+ RepHumanoid.Changed:Connect(function()
1479
+ eventHum = RepHumanoid.Value
1480
+ onHook()
1481
+ end)
1482
+
1483
+ RepHumanoid.Value = Humanoid
1484
+
1485
+ -- setup emote chat hook
1486
+ game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
1487
+ local emote = ""
1488
+ if msg == "/e dance" then
1489
+ emote = dances[math.random(1, #dances)]
1490
+ elseif (string.sub(msg, 1, 3) == "/e ") then
1491
+ emote = string.sub(msg, 4)
1492
+ elseif (string.sub(msg, 1, 7) == "/emote ") then
1493
+ emote = string.sub(msg, 8)
1494
+ end
1495
+
1496
+ if (pose == "Standing" and emoteNames[emote] ~= nil) then
1497
+ playAnimation(emote, 0.1, Humanoid)
1498
+ end
1499
+
1500
+ end)
1501
+
1502
+
1503
+ -- main program
1504
+
1505
+ -- initialize to idle
1506
+ playAnimation("idle", 0.1, Humanoid)
1507
+ pose = "Standing"
1508
+
1509
+ spawn(function()
1510
+ while Figure.Parent ~= nil do
1511
+ local _, time = wait(0.1)
1512
+ move(time)
1513
+ end
1514
+ end)
1515
+
1516
+ return {
1517
+ onRunning = onRunning,
1518
+ onDied = onDied,
1519
+ onJumping = onJumping,
1520
+ onClimbing = onClimbing,
1521
+ onGettingUp = onGettingUp,
1522
+ onFreeFall = onFreeFall,
1523
+ onFallingDown = onFallingDown,
1524
+ onSeated = onSeated,
1525
+ onPlatformStanding = onPlatformStanding,
1526
+ onHook = onHook,
1527
+ onUnhook = onUnhook
1528
+ }</string>
1529
+ <int64 name="SourceAssetId">-1</int64>
1530
+ <BinaryString name="Tags"></BinaryString>
1531
+ </Properties>
1532
+ </Item>
1533
+ <Item class="ObjectValue" referent="8">
1534
+ <Properties>
1535
+ <string name="Name">ReplicatedHumanoid</string>
1536
+ <BinaryString name="AttributesSerialize"></BinaryString>
1537
+ <bool name="DefinesCapabilities">false</bool>
1538
+ <int64 name="SourceAssetId">-1</int64>
1539
+ <BinaryString name="Tags"></BinaryString>
1540
+ <Ref name="Value">null</Ref>
1541
+ </Properties>
1542
+ </Item>
1543
+ <Item class="ModuleScript" referent="9">
1544
+ <Properties>
1545
+ <string name="Name">VerifyAnims</string>
1546
+ <BinaryString name="AttributesSerialize"></BinaryString>
1547
+ <bool name="DefinesCapabilities">false</bool>
1548
+ <Content name="LinkedSource">
1549
+ <null>
1550
+ </null>
1551
+ </Content>
1552
+ <string name="Source">local LENGTH = string.len("Animation")
1553
+
1554
+ local DESC_ANIM_PROPS = {
1555
+ ["ClimbAnimation"] = true,
1556
+ ["FallAnimation"] = true,
1557
+ ["IdleAnimation"] = true,
1558
+ ["JumpAnimation"] = true,
1559
+ ["RunAnimation"] = true,
1560
+ ["SwimAnimation"] = true,
1561
+ ["WalkAnimation"] = true,
1562
+ }
1563
+
1564
+ return function(humanoid, animate)
1565
+ local desc = humanoid:GetAppliedDescription()
1566
+
1567
+ if humanoid.RigType == Enum.HumanoidRigType.R6 then
1568
+ return
1569
+ end
1570
+
1571
+ for prop, _ in pairs(DESC_ANIM_PROPS) do
1572
+ if desc[prop] > 0 then
1573
+ local lookFor = prop:sub(1, #prop - LENGTH):lower()
1574
+ animate:WaitForChild(lookFor)
1575
+ end
1576
+ end
1577
+ end</string>
1578
+ <int64 name="SourceAssetId">-1</int64>
1579
+ <BinaryString name="Tags"></BinaryString>
1580
+ </Properties>
1581
+ </Item>
1582
+ </Item>
1583
+ <Item class="LocalScript" referent="10">
1584
+ <Properties>
1585
+ <string name="Name">PlayerScriptsLoader</string>
1586
+ <BinaryString name="AttributesSerialize"></BinaryString>
1587
+ <bool name="DefinesCapabilities">false</bool>
1588
+ <bool name="Disabled">false</bool>
1589
+ <Content name="LinkedSource">
1590
+ <null>
1591
+ </null>
1592
+ </Content>
1593
+ <token name="RunContext">0</token>
1594
+ <string name="Source">--[[
1595
+ PlayerScriptsLoader - This script requires and instantiates the PlayerModule singleton
1596
+
1597
+ 2018 PlayerScripts Update - AllYourBlox
1598
+ 2020 CameraModule Public Access Override &amp; modifications - EgoMoose
1599
+ --]]
1600
+
1601
+ local MIN_Y = math.rad(-80)
1602
+ local MAX_Y = math.rad(80)
1603
+ local ZERO3 = Vector3.new(0, 0, 0)
1604
+
1605
+ local PlayerModule = script.Parent:WaitForChild("PlayerModule")
1606
+ local CameraInjector = script:WaitForChild("CameraInjector")
1607
+
1608
+ require(CameraInjector)
1609
+
1610
+ -- Control Modifications
1611
+
1612
+ local Control = require(PlayerModule:WaitForChild("ControlModule"))
1613
+ local TouchJump = require(PlayerModule.ControlModule:WaitForChild("TouchJump"))
1614
+
1615
+ function Control:IsJumping()
1616
+ if self.activeController then
1617
+ return self.activeController:GetIsJumping()
1618
+ or (self.touchJumpController and self.touchJumpController:GetIsJumping())
1619
+ end
1620
+ return false
1621
+ end
1622
+
1623
+ local oldEnabled = TouchJump.UpdateEnabled
1624
+
1625
+ function TouchJump:UpdateEnabled()
1626
+ self.jumpStateEnabled = true
1627
+ oldEnabled(self)
1628
+ end
1629
+
1630
+ -- Camera Modifications
1631
+
1632
+ local CameraModule = PlayerModule:WaitForChild("CameraModule")
1633
+
1634
+ local UserSettings = require(script:WaitForChild("FakeUserSettings"))
1635
+ local UserGameSettings = UserSettings():GetService("UserGameSettings")
1636
+
1637
+ local FFlagUserFlagEnableNewVRSystem = UserSettings():SafeIsUserFeatureEnabled("UserFlagEnableNewVRSystem")
1638
+
1639
+ -- Camera variables
1640
+
1641
+ local transitionRate = 0.15
1642
+ local upVector = Vector3.new(0, 1, 0)
1643
+ local upCFrame = CFrame.new()
1644
+
1645
+ local spinPart = workspace.Terrain
1646
+ local prevSpinPart = spinPart
1647
+ local prevSpinCFrame = spinPart.CFrame
1648
+ local twistCFrame = CFrame.new()
1649
+
1650
+ -- Camera Utilities
1651
+
1652
+ local Utils = require(CameraModule:WaitForChild("CameraUtils"))
1653
+
1654
+ function Utils.GetAngleBetweenXZVectors(v1, v2)
1655
+ v1 = upCFrame:VectorToObjectSpace(v1)
1656
+ v2 = upCFrame:VectorToObjectSpace(v2)
1657
+ return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
1658
+ end
1659
+
1660
+ -- Popper Camera
1661
+
1662
+ local Poppercam = require(CameraModule:WaitForChild("Poppercam"))
1663
+ local ZoomController = require(CameraModule:WaitForChild("ZoomController"))
1664
+
1665
+ function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
1666
+ local rotatedFocus = desiredCameraFocus * (desiredCameraCFrame - desiredCameraCFrame.p)
1667
+ local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
1668
+ local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
1669
+ return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
1670
+ end
1671
+
1672
+ -- Base Camera
1673
+
1674
+ local BaseCamera = require(CameraModule:WaitForChild("BaseCamera"))
1675
+
1676
+ function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector, rotateInput)
1677
+ local currLookVector = suppliedLookVector or self:GetCameraLookVector()
1678
+ currLookVector = upCFrame:VectorToObjectSpace(currLookVector)
1679
+
1680
+ local currPitchAngle = math.asin(currLookVector.y)
1681
+ local yTheta = math.clamp(rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
1682
+ local constrainedRotateInput = Vector2.new(rotateInput.x, yTheta)
1683
+ local startCFrame = CFrame.new(ZERO3, currLookVector)
1684
+ local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
1685
+
1686
+ return newLookCFrame
1687
+ end
1688
+
1689
+ function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
1690
+ return self:CalculateNewLookCFrameFromArg(suppliedLookVector, self.rotateInput)
1691
+ end
1692
+
1693
+ local defaultUpdateMouseBehavior = BaseCamera.UpdateMouseBehavior
1694
+
1695
+ function BaseCamera:UpdateMouseBehavior()
1696
+ defaultUpdateMouseBehavior(self)
1697
+ if UserGameSettings.RotationType == Enum.RotationType.CameraRelative then
1698
+ UserGameSettings.RotationType = Enum.RotationType.MovementRelative
1699
+ end
1700
+ end
1701
+
1702
+ -- Vehicle Camera
1703
+
1704
+ local VehicleCamera = require(CameraModule:WaitForChild("VehicleCamera"))
1705
+ local VehicleCameraCore = require(CameraModule.VehicleCamera:WaitForChild("VehicleCameraCore"))
1706
+ local setTransform = VehicleCameraCore.setTransform
1707
+
1708
+ function VehicleCameraCore:setTransform(transform)
1709
+ transform = upCFrame:ToObjectSpace(transform - transform.p) + transform.p
1710
+ return setTransform(self, transform)
1711
+ end
1712
+
1713
+ -- Camera Module
1714
+
1715
+ local function getRotationBetween(u, v, axis)
1716
+ local dot, uxv = u:Dot(v), u:Cross(v)
1717
+ if dot &lt; -0.99999 then return CFrame.fromAxisAngle(axis, math.pi) end
1718
+ return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
1719
+ end
1720
+
1721
+ local function twistAngle(cf, direction)
1722
+ local axis, theta = cf:ToAxisAngle()
1723
+ local w, v = math.cos(theta/2), math.sin(theta/2)*axis
1724
+ local proj = v:Dot(direction)*direction
1725
+ local twist = CFrame.new(0, 0, 0, proj.x, proj.y, proj.z, w)
1726
+ local nAxis, nTheta = twist:ToAxisAngle()
1727
+ return math.sign(v:Dot(direction))*nTheta
1728
+ end
1729
+
1730
+ local function calculateUpCFrame(self)
1731
+ local newUpVector = self:GetUpVector(upVector)
1732
+
1733
+ local axis = workspace.CurrentCamera.CFrame.RightVector
1734
+ local sphericalArc = getRotationBetween(upVector, newUpVector, axis)
1735
+ local transitionCF = CFrame.new():Lerp(sphericalArc, transitionRate)
1736
+
1737
+ upVector = transitionCF * upVector
1738
+ upCFrame = transitionCF * upCFrame
1739
+ end
1740
+
1741
+ local function calculateSpinCFrame(self)
1742
+ local theta = 0
1743
+
1744
+ if spinPart == prevSpinPart then
1745
+ local rotation = spinPart.CFrame - spinPart.CFrame.p
1746
+ local prevRotation = prevSpinCFrame - prevSpinCFrame.p
1747
+
1748
+ local spinAxis = rotation:VectorToObjectSpace(upVector)
1749
+ theta = twistAngle(prevRotation:ToObjectSpace(rotation), spinAxis)
1750
+ end
1751
+
1752
+ twistCFrame = CFrame.fromEulerAnglesYXZ(0, theta, 0)
1753
+
1754
+ prevSpinPart = spinPart
1755
+ prevSpinCFrame = spinPart.CFrame
1756
+ end
1757
+
1758
+ local Camera = require(CameraModule)
1759
+ local CameraInput = require(CameraModule:WaitForChild("CameraInput"))
1760
+
1761
+ function Camera:GetUpVector(oldUpVector)
1762
+ return oldUpVector
1763
+ end
1764
+
1765
+ function Camera:SetSpinPart(part)
1766
+ spinPart = part
1767
+ end
1768
+
1769
+ function Camera:SetTransitionRate(rate)
1770
+ transitionRate = rate
1771
+ end
1772
+
1773
+ function Camera:GetTransitionRate()
1774
+ return transitionRate
1775
+ end
1776
+
1777
+ function Camera:Update(dt)
1778
+ if self.activeCameraController then
1779
+ self.activeCameraController:UpdateMouseBehavior()
1780
+
1781
+ local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
1782
+ newCameraFocus = CFrame.new(newCameraFocus.p) -- vehicle camera fix
1783
+
1784
+ -- if not FFlagUserFlagEnableNewVRSystem then
1785
+ --self.activeCameraController:ApplyVRTransform()
1786
+ -- end
1787
+
1788
+ calculateUpCFrame(self, dt)
1789
+ calculateSpinCFrame(self)
1790
+
1791
+ local lockOffset = Vector3.new(0, 0, 0)
1792
+ if self.activeMouseLockController and self.activeMouseLockController:GetIsMouseLocked() then
1793
+ lockOffset = self.activeMouseLockController:GetMouseLockOffset()
1794
+ end
1795
+
1796
+ local offset = newCameraFocus:ToObjectSpace(newCameraCFrame)
1797
+ local camRotation = upCFrame * twistCFrame * offset
1798
+ newCameraFocus = newCameraFocus - newCameraCFrame:VectorToWorldSpace(lockOffset) + camRotation:VectorToWorldSpace(lockOffset)
1799
+ newCameraCFrame = newCameraFocus * camRotation
1800
+
1801
+ if self.activeOcclusionModule then
1802
+ newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
1803
+ end
1804
+
1805
+ -- Here is where the new CFrame and Focus are set for this render frame
1806
+ game.Workspace.CurrentCamera.CFrame = newCameraCFrame
1807
+ game.Workspace.CurrentCamera.Focus = newCameraFocus
1808
+
1809
+ -- Update to character local transparency as needed based on camera-to-subject distance
1810
+ if self.activeTransparencyController then
1811
+ self.activeTransparencyController:Update(dt)
1812
+ end
1813
+
1814
+ if CameraInput.getInputEnabled() then
1815
+ CameraInput.resetInputForFrameEnd()
1816
+ end
1817
+ end
1818
+ end
1819
+
1820
+ function Camera:IsFirstPerson()
1821
+ if self.activeCameraController then
1822
+ return self.activeCameraController.inFirstPerson
1823
+ end
1824
+ return false
1825
+ end
1826
+
1827
+ function Camera:IsMouseLocked()
1828
+ if self.activeCameraController then
1829
+ return self.activeCameraController:GetIsMouseLocked()
1830
+ end
1831
+ return false
1832
+ end
1833
+
1834
+ function Camera:IsToggleMode()
1835
+ if self.activeCameraController then
1836
+ return self.activeCameraController.isCameraToggle
1837
+ end
1838
+ return false
1839
+ end
1840
+
1841
+ function Camera:IsCamRelative()
1842
+ return self:IsMouseLocked() or self:IsFirstPerson()
1843
+ --return self:IsToggleMode(), self:IsMouseLocked(), self:IsFirstPerson()
1844
+ end
1845
+
1846
+ --
1847
+
1848
+ require(PlayerModule)</string>
1849
+ <int64 name="SourceAssetId">-1</int64>
1850
+ <BinaryString name="Tags"></BinaryString>
1851
+ </Properties>
1852
+ <Item class="ModuleScript" referent="11">
1853
+ <Properties>
1854
+ <string name="Name">CameraInjector</string>
1855
+ <BinaryString name="AttributesSerialize"></BinaryString>
1856
+ <bool name="DefinesCapabilities">false</bool>
1857
+ <Content name="LinkedSource">
1858
+ <null>
1859
+ </null>
1860
+ </Content>
1861
+ <string name="Source"><![CDATA[-- Injects into the CameraModule to override for public API access
1862
+ -- EgoMoose
1863
+
1864
+ local FakeUserSettingsFunc = require(script.Parent:WaitForChild("FakeUserSettings"))
1865
+
1866
+ -- Camera Injection
1867
+
1868
+ local PlayerModule = script.Parent.Parent:WaitForChild("PlayerModule")
1869
+ local CameraModule = PlayerModule:WaitForChild("CameraModule")
1870
+ local TransparencyController = require(CameraModule:WaitForChild("TransparencyController"))
1871
+
1872
+ local result = nil
1873
+ local copy = TransparencyController.Enable
1874
+ local bind = Instance.new("BindableEvent")
1875
+
1876
+ local CameraModuleSelf = nil
1877
+ local CameraModuleMeta = nil
1878
+
1879
+ local metasetmetatable = function(newTable:{}, newMeta:{})
1880
+ local env = getfenv(2)
1881
+ if env.script==CameraModule then
1882
+ CameraModuleSelf = newTable
1883
+ CameraModuleMeta = newMeta
1884
+ end
1885
+ return setmetatable(newTable, newMeta)
1886
+ end
1887
+
1888
+ local phaseTwoEnable = function(self, ...)
1889
+ local env = getfenv(3)
1890
+ env.setmetatable = nil
1891
+ setfenv(3, env)
1892
+
1893
+ TransparencyController.Enable = copy
1894
+ return copy(self, ...)
1895
+ end
1896
+
1897
+ TransparencyController.Enable = function(self, ...)
1898
+ copy(self, ...)
1899
+
1900
+ local env = getfenv(3)
1901
+ env.UserSettings = FakeUserSettingsFunc
1902
+ env.setmetatable = metasetmetatable
1903
+ local f = setfenv(3, env)
1904
+
1905
+ TransparencyController.Enable = phaseTwoEnable
1906
+
1907
+ result = f()
1908
+ if result.ActivateCameraController==nil
1909
+ and typeof(CameraModuleSelf)=="table"
1910
+ and typeof(CameraModuleSelf.ActivateCameraController)=="function" then
1911
+ result = CameraModuleSelf
1912
+ end
1913
+ bind.Event:Wait() -- infinite wait so no more connections can be made
1914
+ end
1915
+
1916
+ coroutine.wrap(function()
1917
+ require(CameraModule)
1918
+ end)()
1919
+
1920
+ -- Place children under injection
1921
+
1922
+ for _, child in pairs(CameraModule:GetChildren()) do
1923
+ child.Parent = script
1924
+ end
1925
+
1926
+ CameraModule.Name = "_CameraModule"
1927
+ script.Name = "CameraModule"
1928
+ script.Parent = PlayerModule
1929
+
1930
+ --
1931
+
1932
+ return result
1933
+ ]]></string>
1934
+ <int64 name="SourceAssetId">-1</int64>
1935
+ <BinaryString name="Tags"></BinaryString>
1936
+ </Properties>
1937
+ </Item>
1938
+ <Item class="ModuleScript" referent="12">
1939
+ <Properties>
1940
+ <string name="Name">FakeUserSettings</string>
1941
+ <BinaryString name="AttributesSerialize"></BinaryString>
1942
+ <bool name="DefinesCapabilities">false</bool>
1943
+ <Content name="LinkedSource">
1944
+ <null>
1945
+ </null>
1946
+ </Content>
1947
+ <string name="Source">local FFLAG_OVERRIDES = {
1948
+ ["UserRemoveTheCameraApi"] = false
1949
+ }
1950
+
1951
+ local FakeUserSettings = {}
1952
+
1953
+ function FakeUserSettings:IsUserFeatureEnabled(name)
1954
+ if FFLAG_OVERRIDES[name] ~= nil then
1955
+ return FFLAG_OVERRIDES[name]
1956
+ end
1957
+ return UserSettings():IsUserFeatureEnabled(name)
1958
+ end
1959
+
1960
+ function FakeUserSettings:SafeIsUserFeatureEnabled(name)
1961
+ local success, result = pcall(function()
1962
+ return self:IsUserFeatureEnabled(name)
1963
+ end)
1964
+ return success and result
1965
+ end
1966
+
1967
+ function FakeUserSettings:GetService(name)
1968
+ return UserSettings():GetService(name)
1969
+ end
1970
+
1971
+ local function FakeUserSettingsFunc()
1972
+ return FakeUserSettings
1973
+ end
1974
+
1975
+ return FakeUserSettingsFunc</string>
1976
+ <int64 name="SourceAssetId">-1</int64>
1977
+ <BinaryString name="Tags"></BinaryString>
1978
+ </Properties>
1979
+ </Item>
1980
+ </Item>
1981
+ <Item class="LocalScript" referent="13">
1982
+ <Properties>
1983
+ <string name="Name">RbxCharacterSounds</string>
1984
+ <BinaryString name="AttributesSerialize"></BinaryString>
1985
+ <bool name="DefinesCapabilities">false</bool>
1986
+ <bool name="Disabled">false</bool>
1987
+ <Content name="LinkedSource">
1988
+ <null>
1989
+ </null>
1990
+ </Content>
1991
+ <token name="RunContext">0</token>
1992
+ <string name="Source"><![CDATA[-- Roblox character sound script
1993
+
1994
+ local Players = game:GetService("Players")
1995
+ local RunService = game:GetService("RunService")
1996
+
1997
+ local AnimationState = require(script:WaitForChild("AnimationState"))
1998
+
1999
+ local SOUND_DATA = {
2000
+ Climbing = {
2001
+ SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
2002
+ Looped = true,
2003
+ },
2004
+ Died = {
2005
+ SoundId = "rbxasset://sounds/uuhhh.mp3",
2006
+ },
2007
+ FreeFalling = {
2008
+ SoundId = "rbxasset://sounds/action_falling.mp3",
2009
+ Looped = true,
2010
+ },
2011
+ GettingUp = {
2012
+ SoundId = "rbxasset://sounds/action_get_up.mp3",
2013
+ },
2014
+ Jumping = {
2015
+ SoundId = "rbxasset://sounds/action_jump.mp3",
2016
+ },
2017
+ Landing = {
2018
+ SoundId = "rbxasset://sounds/action_jump_land.mp3",
2019
+ },
2020
+ Running = {
2021
+ SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
2022
+ Looped = true,
2023
+ Pitch = 1.85,
2024
+ },
2025
+ Splash = {
2026
+ SoundId = "rbxasset://sounds/impact_water.mp3",
2027
+ },
2028
+ Swimming = {
2029
+ SoundId = "rbxasset://sounds/action_swim.mp3",
2030
+ Looped = true,
2031
+ Pitch = 1.6,
2032
+ },
2033
+ }
2034
+
2035
+ -- wait for the first of the passed signals to fire
2036
+ local function waitForFirst(...)
2037
+ local shunt = Instance.new("BindableEvent")
2038
+ local slots = {...}
2039
+
2040
+ local function fire(...)
2041
+ for i = 1, #slots do
2042
+ slots[i]:Disconnect()
2043
+ end
2044
+
2045
+ return shunt:Fire(...)
2046
+ end
2047
+
2048
+ for i = 1, #slots do
2049
+ slots[i] = slots[i]:Connect(fire)
2050
+ end
2051
+
2052
+ return shunt.Event:Wait()
2053
+ end
2054
+
2055
+ -- map a value from one range to another
2056
+ local function map(x, inMin, inMax, outMin, outMax)
2057
+ return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
2058
+ end
2059
+
2060
+ local function playSound(sound)
2061
+ sound.TimePosition = 0
2062
+ sound.Playing = true
2063
+ end
2064
+
2065
+ local function shallowCopy(t)
2066
+ local out = {}
2067
+ for k, v in pairs(t) do
2068
+ out[k] = v
2069
+ end
2070
+ return out
2071
+ end
2072
+
2073
+ local function initializeSoundSystem(player, humanoid, rootPart)
2074
+ local sounds = {}
2075
+
2076
+ -- initialize sounds
2077
+ for name, props in pairs(SOUND_DATA) do
2078
+ local sound = Instance.new("Sound")
2079
+ sound.Name = name
2080
+
2081
+ -- set default values
2082
+ sound.Archivable = false
2083
+ sound.EmitterSize = 5
2084
+ sound.MaxDistance = 150
2085
+ sound.Volume = 0.65
2086
+
2087
+ for propName, propValue in pairs(props) do
2088
+ sound[propName] = propValue
2089
+ end
2090
+
2091
+ sound.Parent = rootPart
2092
+ sounds[name] = sound
2093
+ end
2094
+
2095
+ local playingLoopedSounds = {}
2096
+
2097
+ local function stopPlayingLoopedSounds(except)
2098
+ for sound in pairs(shallowCopy(playingLoopedSounds)) do
2099
+ if sound ~= except then
2100
+ sound.Playing = false
2101
+ playingLoopedSounds[sound] = nil
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ -- state transition callbacks
2107
+ local stateTransitions = {
2108
+ [Enum.HumanoidStateType.FallingDown] = function()
2109
+ stopPlayingLoopedSounds()
2110
+ end,
2111
+
2112
+ [Enum.HumanoidStateType.GettingUp] = function()
2113
+ stopPlayingLoopedSounds()
2114
+ playSound(sounds.GettingUp)
2115
+ end,
2116
+
2117
+ [Enum.HumanoidStateType.Jumping] = function()
2118
+ stopPlayingLoopedSounds()
2119
+ playSound(sounds.Jumping)
2120
+ end,
2121
+
2122
+ [Enum.HumanoidStateType.Swimming] = function()
2123
+ local verticalSpeed = math.abs(rootPart.Velocity.Y)
2124
+ if verticalSpeed > 0.1 then
2125
+ sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
2126
+ playSound(sounds.Splash)
2127
+ end
2128
+ stopPlayingLoopedSounds(sounds.Swimming)
2129
+ sounds.Swimming.Playing = true
2130
+ playingLoopedSounds[sounds.Swimming] = true
2131
+ end,
2132
+
2133
+ [Enum.HumanoidStateType.Freefall] = function()
2134
+ sounds.FreeFalling.Volume = 0
2135
+ stopPlayingLoopedSounds(sounds.FreeFalling)
2136
+ playingLoopedSounds[sounds.FreeFalling] = true
2137
+ end,
2138
+
2139
+ [Enum.HumanoidStateType.Landed] = function()
2140
+ stopPlayingLoopedSounds()
2141
+ local verticalSpeed = math.abs(rootPart.Velocity.Y)
2142
+ if verticalSpeed > 75 then
2143
+ sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
2144
+ playSound(sounds.Landing)
2145
+ end
2146
+ end,
2147
+
2148
+ [Enum.HumanoidStateType.Running] = function()
2149
+ stopPlayingLoopedSounds(sounds.Running)
2150
+ sounds.Running.Playing = true
2151
+ playingLoopedSounds[sounds.Running] = true
2152
+ end,
2153
+
2154
+ [Enum.HumanoidStateType.Climbing] = function()
2155
+ local sound = sounds.Climbing
2156
+ if math.abs(rootPart.Velocity.Y) > 0.1 then
2157
+ sound.Playing = true
2158
+ stopPlayingLoopedSounds(sound)
2159
+ else
2160
+ stopPlayingLoopedSounds()
2161
+ end
2162
+ playingLoopedSounds[sound] = true
2163
+ end,
2164
+
2165
+ [Enum.HumanoidStateType.Seated] = function()
2166
+ stopPlayingLoopedSounds()
2167
+ end,
2168
+
2169
+ [Enum.HumanoidStateType.Dead] = function()
2170
+ stopPlayingLoopedSounds()
2171
+ playSound(sounds.Died)
2172
+ end,
2173
+ }
2174
+
2175
+ -- updaters for looped sounds
2176
+ local loopedSoundUpdaters = {
2177
+ [sounds.Climbing] = function(dt, sound, vel)
2178
+ sound.Playing = vel.Magnitude > 0.1
2179
+ end,
2180
+
2181
+ [sounds.FreeFalling] = function(dt, sound, vel)
2182
+ if vel.Magnitude > 75 then
2183
+ sound.Volume = math.clamp(sound.Volume + 0.9*dt, 0, 1)
2184
+ else
2185
+ sound.Volume = 0
2186
+ end
2187
+ end,
2188
+
2189
+ [sounds.Running] = function(dt, sound, vel)
2190
+ sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
2191
+ end,
2192
+ }
2193
+
2194
+ -- state substitutions to avoid duplicating entries in the state table
2195
+ local stateRemap = {
2196
+ [Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
2197
+ }
2198
+
2199
+ local activeState = stateRemap[humanoid:GetState()] or humanoid:GetState()
2200
+ local animator = humanoid:WaitForChild("Animator")
2201
+
2202
+ local function onStateChange(_, state)
2203
+ state = stateRemap[state] or state
2204
+
2205
+ if state ~= activeState then
2206
+ local transitionFunc = stateTransitions[state]
2207
+
2208
+ if transitionFunc then
2209
+ transitionFunc()
2210
+ end
2211
+
2212
+ activeState = state
2213
+ end
2214
+ end
2215
+
2216
+ local stateChangedConn = humanoid.StateChanged:Connect(onStateChange)
2217
+ local animStateChangedConn = AnimationState(animator, function(_, state)
2218
+ if humanoid.PlatformStand then
2219
+ onStateChange(_, state)
2220
+ end
2221
+ end)
2222
+
2223
+ local steppedConn = RunService.Stepped:Connect(function(_, worldDt)
2224
+ -- update looped sounds on stepped
2225
+ for sound in pairs(playingLoopedSounds) do
2226
+ local updater = loopedSoundUpdaters[sound]
2227
+
2228
+ if updater then
2229
+ updater(worldDt, sound, rootPart.Velocity)
2230
+ end
2231
+ end
2232
+ end)
2233
+
2234
+ local humanoidAncestryChangedConn
2235
+ local rootPartAncestryChangedConn
2236
+ local characterAddedConn
2237
+
2238
+ local function terminate()
2239
+ stateChangedConn:Disconnect()
2240
+ animStateChangedConn:Disconnect()
2241
+ steppedConn:Disconnect()
2242
+ humanoidAncestryChangedConn:Disconnect()
2243
+ rootPartAncestryChangedConn:Disconnect()
2244
+ characterAddedConn:Disconnect()
2245
+ end
2246
+
2247
+ humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
2248
+ if not parent then
2249
+ terminate()
2250
+ end
2251
+ end)
2252
+
2253
+ rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
2254
+ if not parent then
2255
+ terminate()
2256
+ end
2257
+ end)
2258
+
2259
+ characterAddedConn = player.CharacterAdded:Connect(terminate)
2260
+ end
2261
+
2262
+ local function playerAdded(player)
2263
+ local function characterAdded(character)
2264
+ -- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
2265
+ -- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
2266
+ -- ** must use a waitForFirst on everything and listen for hierarchy changes.
2267
+ -- * the character might not be in the dm by the time CharacterAdded fires
2268
+ -- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
2269
+ -- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
2270
+ -- * RootPart probably won't exist immediately.
2271
+ -- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.
2272
+
2273
+ if not character.Parent then
2274
+ waitForFirst(character.AncestryChanged, player.CharacterAdded)
2275
+ end
2276
+
2277
+ if player.Character ~= character or not character.Parent then
2278
+ return
2279
+ end
2280
+
2281
+ local humanoid = character:FindFirstChildOfClass("Humanoid")
2282
+ while character:IsDescendantOf(game) and not humanoid do
2283
+ waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
2284
+ humanoid = character:FindFirstChildOfClass("Humanoid")
2285
+ end
2286
+
2287
+ if player.Character ~= character or not character:IsDescendantOf(game) then
2288
+ return
2289
+ end
2290
+
2291
+ -- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
2292
+ local rootPart = character:FindFirstChild("HumanoidRootPart")
2293
+ while character:IsDescendantOf(game) and not rootPart do
2294
+ waitForFirst(character.ChildAdded, character.AncestryChanged, humanoid.AncestryChanged, player.CharacterAdded)
2295
+ rootPart = character:FindFirstChild("HumanoidRootPart")
2296
+ end
2297
+
2298
+ if rootPart and humanoid:IsDescendantOf(game) and character:IsDescendantOf(game) and player.Character == character then
2299
+ initializeSoundSystem(player, humanoid, rootPart)
2300
+ end
2301
+ end
2302
+
2303
+ if player.Character then
2304
+ characterAdded(player.Character)
2305
+ end
2306
+ player.CharacterAdded:Connect(characterAdded)
2307
+ end
2308
+
2309
+ Players.PlayerAdded:Connect(playerAdded)
2310
+ for _, player in ipairs(Players:GetPlayers()) do
2311
+ playerAdded(player)
2312
+ end
2313
+ ]]></string>
2314
+ <int64 name="SourceAssetId">-1</int64>
2315
+ <BinaryString name="Tags"></BinaryString>
2316
+ </Properties>
2317
+ <Item class="ModuleScript" referent="14">
2318
+ <Properties>
2319
+ <string name="Name">AnimationState</string>
2320
+ <BinaryString name="AttributesSerialize"></BinaryString>
2321
+ <bool name="DefinesCapabilities">false</bool>
2322
+ <Content name="LinkedSource">
2323
+ <null>
2324
+ </null>
2325
+ </Content>
2326
+ <string name="Source">local STATE_MAP = {
2327
+ ["climb"] = Enum.HumanoidStateType.Climbing,
2328
+ ["fall"] = Enum.HumanoidStateType.Freefall,
2329
+ ["idle"] = Enum.HumanoidStateType.RunningNoPhysics,
2330
+ ["jump"] = Enum.HumanoidStateType.Jumping,
2331
+ ["run"] = Enum.HumanoidStateType.Running,
2332
+ ["swim"] = Enum.HumanoidStateType.Swimming,
2333
+ ["swimidle"] = Enum.HumanoidStateType.Swimming,
2334
+ ["walk"] = Enum.HumanoidStateType.Running,
2335
+ }
2336
+
2337
+ return function(animator, callback)
2338
+ local humanoid = animator.Parent
2339
+ local prevState = humanoid:GetState()
2340
+
2341
+ return animator.AnimationPlayed:Connect(function(track)
2342
+ local state = STATE_MAP[track.Name]
2343
+
2344
+ if not state then
2345
+ local container = track.Animation.Parent
2346
+ state = container and STATE_MAP[container.Name]
2347
+ end
2348
+
2349
+ if state then
2350
+ callback(prevState, state)
2351
+ prevState = state
2352
+ end
2353
+ end)
2354
+ end</string>
2355
+ <int64 name="SourceAssetId">-1</int64>
2356
+ <BinaryString name="Tags"></BinaryString>
2357
+ </Properties>
2358
+ </Item>
2359
+ </Item>
2360
+ </Item>
2361
+ <Item class="ModuleScript" referent="15">
2362
+ <Properties>
2363
+ <string name="Name">GravityController</string>
2364
+ <BinaryString name="AttributesSerialize"></BinaryString>
2365
+ <bool name="DefinesCapabilities">false</bool>
2366
+ <Content name="LinkedSource">
2367
+ <null>
2368
+ </null>
2369
+ </Content>
2370
+ <string name="Source">local RunService = game:GetService("RunService")
2371
+
2372
+ local Utility = script:WaitForChild("Utility")
2373
+ local CharacterModules = script:WaitForChild("CharacterModules")
2374
+
2375
+ local Maid = require(Utility:WaitForChild("Maid"))
2376
+ local Signal = require(Utility:WaitForChild("Signal"))
2377
+ local Camera = require(CharacterModules:WaitForChild("Camera"))
2378
+ local Control = require(CharacterModules:WaitForChild("Control"))
2379
+ local Collider = require(script:WaitForChild("Collider"))
2380
+ local StateTracker = require(script:WaitForChild("StateTracker"))
2381
+
2382
+ -- CONSTANTS
2383
+
2384
+ local TRANSITION = 0.15
2385
+ local WALK_FORCE = 200 / 3
2386
+ local JUMP_MODIFIER = 1.2
2387
+
2388
+ local ZERO3 = Vector3.new(0, 0, 0)
2389
+ local UNIT_Y = Vector3.new(0, 1, 0)
2390
+
2391
+ -- Class
2392
+
2393
+ local GravityControllerClass = {}
2394
+ GravityControllerClass.__index = GravityControllerClass
2395
+ GravityControllerClass.ClassName = "GravityController"
2396
+
2397
+ -- Public Constructors
2398
+
2399
+ function GravityControllerClass.new(player)
2400
+ local self = setmetatable({}, GravityControllerClass)
2401
+
2402
+ self.Player = player
2403
+ self.Character = player.Character
2404
+ self.Humanoid = player.Character:WaitForChild("Humanoid")
2405
+ self.HRP = self.Humanoid.RootPart
2406
+
2407
+ self._gravityUp = UNIT_Y
2408
+ self._characterMass = 0
2409
+
2410
+ self._camera = Camera.new(self)
2411
+ self._control = Control.new(self)
2412
+ self._collider = Collider.new(self)
2413
+
2414
+ self._fallStart = self.HRP.Position.y
2415
+ self._prevPart = workspace.Terrain
2416
+ self._prevCFrame = CFrame.new()
2417
+
2418
+ self.StateTracker = StateTracker.new(self)
2419
+ self.Maid = Maid.new()
2420
+
2421
+ init(self)
2422
+
2423
+ return self
2424
+ end
2425
+
2426
+ -- Private Methods
2427
+
2428
+ local function getRotationBetween(u, v, axis)
2429
+ local dot, uxv = u:Dot(v), u:Cross(v)
2430
+ if dot &lt; -0.99999 then return CFrame.fromAxisAngle(axis, math.pi) end
2431
+ return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
2432
+ end
2433
+
2434
+ local function getModelMass(model)
2435
+ local mass = 0
2436
+ for _, part in pairs(model:GetDescendants()) do
2437
+ if part:IsA("BasePart") and not part.Massless then
2438
+ mass = mass + part:GetMass()
2439
+ end
2440
+ end
2441
+ return mass
2442
+ end
2443
+
2444
+ local function onJumpRequest(self)
2445
+ if not self.StateTracker.Jumped and self._collider:IsGrounded(true) then
2446
+ local vel = self.HRP.Velocity
2447
+ self.HRP.Velocity = vel + self._gravityUp*self.Humanoid.JumpPower*JUMP_MODIFIER
2448
+ self.StateTracker:RequestJump()
2449
+ end
2450
+ end
2451
+
2452
+ local function onHeartbeat(self, dt)
2453
+ local standingPart = self._collider:GetStandingPart()
2454
+
2455
+ if standingPart and self._prevPart and self._prevPart == standingPart then
2456
+ local offset = self._prevCFrame:ToObjectSpace(self.HRP.CFrame)
2457
+ self.HRP.CFrame = standingPart.CFrame * offset
2458
+ end
2459
+
2460
+ self._prevPart = standingPart
2461
+ self._prevCFrame = standingPart and standingPart.CFrame
2462
+ end
2463
+
2464
+ local function onGravityStep(self, dt)
2465
+ local camCF = workspace.CurrentCamera.CFrame
2466
+
2467
+ -- update the gravity vector
2468
+ local oldGravity = self._gravityUp
2469
+ local newGravity = self:GetGravityUp(oldGravity)
2470
+
2471
+ local sphericalArc = getRotationBetween(oldGravity, newGravity, camCF.XVector)
2472
+ local lerpedArc = CFrame.new():Lerp(sphericalArc, TRANSITION)
2473
+
2474
+ self._gravityUp = lerpedArc * oldGravity
2475
+
2476
+ -- get world move vector
2477
+ local fDot = camCF.ZVector:Dot(newGravity)
2478
+ local cForward = math.abs(fDot) > 0.5 and math.sign(fDot)*camCF.YVector or -camCF.ZVector
2479
+
2480
+ local left = -cForward:Cross(newGravity).Unit
2481
+ local forward = -left:Cross(newGravity).Unit
2482
+
2483
+ local move = self._control:GetMoveVector()
2484
+ local worldMove = forward*move.z - left*move.x
2485
+
2486
+ local isInputMoving = false
2487
+ local length = worldMove.Magnitude
2488
+ if length > 0 then
2489
+ isInputMoving = true
2490
+ worldMove = worldMove / length
2491
+ end
2492
+
2493
+ -- get the desired character cframe
2494
+ local hrpLook = -self.HRP.CFrame.ZVector
2495
+ local charForward = hrpLook:Dot(forward)*forward + hrpLook:Dot(left)*left
2496
+ local charRight = charForward:Cross(newGravity).Unit
2497
+
2498
+ local newCharRotation = CFrame.new()
2499
+ local newCharCF = CFrame.fromMatrix(ZERO3, charRight, newGravity, -charForward)
2500
+
2501
+ if self._camera.CameraModule:IsCamRelative() then
2502
+ newCharCF = CFrame.fromMatrix(ZERO3, -left, newGravity)
2503
+ elseif isInputMoving then
2504
+ newCharRotation = newCharRotation:Lerp(getRotationBetween(
2505
+ charForward,
2506
+ worldMove,
2507
+ newGravity
2508
+ ), 0.7)
2509
+ end
2510
+
2511
+ -- calculate forces
2512
+ local g = workspace.Gravity
2513
+ local gForce = g * self._characterMass * (UNIT_Y - newGravity)
2514
+
2515
+ local cVelocity = self.HRP.Velocity
2516
+ local tVelocity = self.Humanoid.WalkSpeed * worldMove
2517
+ local gVelocity = cVelocity:Dot(newGravity)*newGravity
2518
+ local hVelocity = cVelocity - gVelocity
2519
+
2520
+ if hVelocity:Dot(hVelocity) &lt; 1 then
2521
+ hVelocity = ZERO3
2522
+ end
2523
+
2524
+ local dVelocity = tVelocity - hVelocity
2525
+ local dVelocityM = dVelocity.Magnitude
2526
+
2527
+ local walkForceM = math.min(10000, WALK_FORCE * self._characterMass * dVelocityM / (dt*60))
2528
+ local walkForce = walkForceM > 0 and (dVelocity / dVelocityM)*walkForceM or ZERO3
2529
+
2530
+ local charRotation = newCharRotation * newCharCF
2531
+
2532
+ self.StateTracker:Update(self._gravityUp, self._collider:IsGrounded(false), isInputMoving)
2533
+ self._collider:Update(walkForce + gForce, charRotation)
2534
+ end
2535
+
2536
+ function init(self)
2537
+ self.Maid:Mark(self._camera)
2538
+ self.Maid:Mark(self._control)
2539
+ self.Maid:Mark(self._collider)
2540
+
2541
+ self._characterMass = getModelMass(self.Character)
2542
+ self.Maid:Mark(self.Character.AncestryChanged:Connect(function()
2543
+ self._characterMass = getModelMass(self.Character)
2544
+ end))
2545
+
2546
+ self.Humanoid.PlatformStand = true
2547
+ self.Maid:Mark(self.Humanoid:GetPropertyChangedSignal("Jump"):Connect(function()
2548
+ if self.Humanoid.Jump then
2549
+ onJumpRequest(self)
2550
+ self.Humanoid.Jump = false
2551
+ end
2552
+ end))
2553
+
2554
+ self.Maid:Mark(self.StateTracker.Changed:Connect(function(state, speed)
2555
+ if state == Enum.HumanoidStateType.Freefall then
2556
+ self._fallStart = self.HRP.Position:Dot(self._gravityUp)
2557
+ end
2558
+ end))
2559
+
2560
+ self.Maid:Mark(RunService.Heartbeat:Connect(function(dt)
2561
+ onHeartbeat(self, dt)
2562
+ end))
2563
+
2564
+ RunService:BindToRenderStep("GravityStep", Enum.RenderPriority.Camera.Value - 1, function(dt)
2565
+ onGravityStep(self, dt)
2566
+ end)
2567
+
2568
+ self.Humanoid.StateChanged:Wait()
2569
+ self.StateTracker.Changed:Fire(self.StateTracker.State, 0)
2570
+ end
2571
+
2572
+ -- Public Methods
2573
+
2574
+ function GravityControllerClass:ResetGravity(gravity)
2575
+ self._gravityUp = gravity
2576
+ self._fallStart = self.HRP.Position:Dot(gravity)
2577
+ end
2578
+
2579
+ function GravityControllerClass:GetFallHeight()
2580
+ if self.StateTracker.State == Enum.HumanoidStateType.Freefall then
2581
+ local height = self.HRP.Position:Dot(self._gravityUp)
2582
+ return height - self._fallStart
2583
+ end
2584
+ return 0
2585
+ end
2586
+
2587
+ function GravityControllerClass:GetGravityUp(oldGravity)
2588
+ return oldGravity
2589
+ end
2590
+
2591
+ function GravityControllerClass:Destroy()
2592
+ RunService:UnbindFromRenderStep("GravityStep")
2593
+ self.Maid:Sweep()
2594
+ self.Humanoid.PlatformStand = false
2595
+ end
2596
+
2597
+ --
2598
+
2599
+ return GravityControllerClass</string>
2600
+ <int64 name="SourceAssetId">-1</int64>
2601
+ <BinaryString name="Tags"></BinaryString>
2602
+ </Properties>
2603
+ <Item class="Folder" referent="16">
2604
+ <Properties>
2605
+ <string name="Name">CharacterModules</string>
2606
+ <BinaryString name="AttributesSerialize"></BinaryString>
2607
+ <bool name="DefinesCapabilities">false</bool>
2608
+ <int64 name="SourceAssetId">-1</int64>
2609
+ <BinaryString name="Tags"></BinaryString>
2610
+ </Properties>
2611
+ <Item class="ModuleScript" referent="17">
2612
+ <Properties>
2613
+ <string name="Name">Camera</string>
2614
+ <BinaryString name="AttributesSerialize"></BinaryString>
2615
+ <bool name="DefinesCapabilities">false</bool>
2616
+ <Content name="LinkedSource">
2617
+ <null>
2618
+ </null>
2619
+ </Content>
2620
+ <string name="Source">-- Class
2621
+
2622
+ local CameraClass = {}
2623
+ CameraClass.__index = CameraClass
2624
+ CameraClass.ClassName = "Camera"
2625
+
2626
+ -- Public Constructors
2627
+
2628
+ function CameraClass.new(controller)
2629
+ local self = setmetatable({}, CameraClass)
2630
+
2631
+ local player = controller.Player
2632
+ local playerModule = require(player.PlayerScripts:WaitForChild("PlayerModule"))
2633
+
2634
+ self.Controller = controller
2635
+ self.CameraModule = playerModule:GetCameras()
2636
+
2637
+ init(self)
2638
+
2639
+ return self
2640
+ end
2641
+
2642
+ -- Private methods
2643
+
2644
+ function init(self)
2645
+ --self.CameraModule:SetTransitionRate(1)
2646
+ function self.CameraModule.GetUpVector(this, upVector)
2647
+ return self.Controller._gravityUp
2648
+ end
2649
+ end
2650
+
2651
+ -- Public Methods
2652
+
2653
+ function CameraClass:Destroy()
2654
+ function self.CameraModule.GetUpVector(this, upVector)
2655
+ return Vector3.new(0, 1, 0)
2656
+ end
2657
+ end
2658
+
2659
+ --
2660
+
2661
+ return CameraClass</string>
2662
+ <int64 name="SourceAssetId">-1</int64>
2663
+ <BinaryString name="Tags"></BinaryString>
2664
+ </Properties>
2665
+ </Item>
2666
+ <Item class="ModuleScript" referent="18">
2667
+ <Properties>
2668
+ <string name="Name">Control</string>
2669
+ <BinaryString name="AttributesSerialize"></BinaryString>
2670
+ <bool name="DefinesCapabilities">false</bool>
2671
+ <Content name="LinkedSource">
2672
+ <null>
2673
+ </null>
2674
+ </Content>
2675
+ <string name="Source">-- Class
2676
+
2677
+ local ControlClass = {}
2678
+ ControlClass.__index = ControlClass
2679
+ ControlClass.ClassName = "Control"
2680
+
2681
+ -- Public Constructors
2682
+
2683
+ function ControlClass.new(controller)
2684
+ local self = setmetatable({}, ControlClass)
2685
+
2686
+ local player = controller.Player
2687
+ local playerModule = require(player.PlayerScripts:WaitForChild("PlayerModule"))
2688
+
2689
+ self.Controller = controller
2690
+ self.ControlModule = playerModule:GetControls()
2691
+
2692
+ return self
2693
+ end
2694
+
2695
+ -- Public Methods
2696
+
2697
+ function ControlClass:GetMoveVector()
2698
+ return self.ControlModule:GetMoveVector()
2699
+ end
2700
+
2701
+ function ControlClass:Destroy()
2702
+
2703
+ end
2704
+
2705
+ --
2706
+
2707
+ return ControlClass</string>
2708
+ <int64 name="SourceAssetId">-1</int64>
2709
+ <BinaryString name="Tags"></BinaryString>
2710
+ </Properties>
2711
+ </Item>
2712
+ </Item>
2713
+ <Item class="ModuleScript" referent="19">
2714
+ <Properties>
2715
+ <string name="Name">Collider</string>
2716
+ <BinaryString name="AttributesSerialize"></BinaryString>
2717
+ <bool name="DefinesCapabilities">false</bool>
2718
+ <Content name="LinkedSource">
2719
+ <null>
2720
+ </null>
2721
+ </Content>
2722
+ <string name="Source"><![CDATA[local Maid = require(script.Parent.Utility.Maid)
2723
+
2724
+ local params = RaycastParams.new()
2725
+ params.FilterType = Enum.RaycastFilterType.Whitelist
2726
+
2727
+ local params2 = RaycastParams.new()
2728
+ params2.FilterType = Enum.RaycastFilterType.Blacklist
2729
+
2730
+ -- CONSTANTS
2731
+
2732
+ local CUSTOM_PHYSICAL = PhysicalProperties.new (0.7, 0, 0, 1, 100)
2733
+
2734
+ -- Class
2735
+
2736
+ local ColliderClass = {}
2737
+ ColliderClass.__index = ColliderClass
2738
+ ColliderClass.ClassName = "Collider"
2739
+
2740
+ -- Public Constructors
2741
+
2742
+ function ColliderClass.new(controller)
2743
+ local self = setmetatable({}, ColliderClass)
2744
+
2745
+ self.Model = Instance.new("Model")
2746
+
2747
+ local sphere, vForce, floor, floor2, gryo = create(self, controller)
2748
+
2749
+ self._maid = Maid.new()
2750
+
2751
+ self.Controller = controller
2752
+
2753
+ self.Sphere = sphere
2754
+ self.VForce = vForce
2755
+ self.FloorDetector = floor
2756
+ self.JumpDetector = floor2
2757
+ self.Gyro = gryo
2758
+
2759
+ init(self)
2760
+
2761
+ return self
2762
+ end
2763
+
2764
+ -- Private Methods
2765
+
2766
+ local function getHipHeight(controller)
2767
+ if controller.Humanoid.RigType == Enum.HumanoidRigType.R15 then
2768
+ return controller.Humanoid.HipHeight + 0.05
2769
+ end
2770
+ return 2
2771
+ end
2772
+
2773
+ local function getAttachement(controller)
2774
+ if controller.Humanoid.RigType == Enum.HumanoidRigType.R15 then
2775
+ return controller.HRP:WaitForChild("RootRigAttachment")
2776
+ end
2777
+ return controller.HRP:WaitForChild("RootAttachment")
2778
+ end
2779
+
2780
+ function create(self, controller)
2781
+ local hipHeight = getHipHeight(controller)
2782
+ local attach = getAttachement(controller)
2783
+
2784
+ local sphere = Instance.new("Part")
2785
+ sphere.Name = "Sphere"
2786
+ sphere.Massless = true
2787
+ sphere.Size = Vector3.new(2, 2, 2)
2788
+ sphere.Shape = Enum.PartType.Ball
2789
+ sphere.Transparency = 1
2790
+ sphere.CustomPhysicalProperties = CUSTOM_PHYSICAL
2791
+
2792
+ local floor = Instance.new("Part")
2793
+ floor.Name = "FloorDectector"
2794
+ floor.CanCollide = false
2795
+ floor.Massless = true
2796
+ floor.Size = Vector3.new(2, 1, 1)
2797
+ floor.Transparency = 1
2798
+
2799
+ local floor2 = Instance.new("Part")
2800
+ floor2.Name = "JumpDectector"
2801
+ floor2.CanCollide = false
2802
+ floor2.Massless = true
2803
+ floor2.Size = Vector3.new(2, 0.2, 1)
2804
+ floor2.Transparency = 1
2805
+
2806
+ local weld = Instance.new("Weld")
2807
+ weld.C0 = CFrame.new(0, -hipHeight, 0.1)
2808
+ weld.Part0 = controller.HRP
2809
+ weld.Part1 = sphere
2810
+ weld.Parent = sphere
2811
+
2812
+ local weld = Instance.new("Weld")
2813
+ weld.C0 = CFrame.new(0, -hipHeight - 1.5, 0)
2814
+ weld.Part0 = controller.HRP
2815
+ weld.Part1 = floor
2816
+ weld.Parent = floor
2817
+
2818
+ local weld = Instance.new("Weld")
2819
+ weld.C0 = CFrame.new(0, -hipHeight - 1.1, 0)
2820
+ weld.Part0 = controller.HRP
2821
+ weld.Part1 = floor2
2822
+ weld.Parent = floor2
2823
+
2824
+ local vForce = Instance.new("VectorForce")
2825
+ vForce.Force = Vector3.new(0, 0, 0)
2826
+ vForce.ApplyAtCenterOfMass = true
2827
+ vForce.RelativeTo = Enum.ActuatorRelativeTo.World
2828
+ vForce.Attachment0 = attach
2829
+ vForce.Parent = controller.HRP
2830
+
2831
+ local gyro = Instance.new("BodyGyro")
2832
+ gyro.P = 25000
2833
+ gyro.MaxTorque = Vector3.new(100000, 100000, 100000)
2834
+ gyro.CFrame = controller.HRP.CFrame
2835
+ gyro.Parent = controller.HRP
2836
+
2837
+ floor.Touched:Connect(function() end)
2838
+ floor2.Touched:Connect(function() end)
2839
+
2840
+ sphere.Parent = self.Model
2841
+ floor.Parent = self.Model
2842
+ floor2.Parent = self.Model
2843
+
2844
+ return sphere, vForce, floor, floor2, gyro
2845
+ end
2846
+
2847
+ function init(self)
2848
+ self._maid:Mark(self.Model)
2849
+ self._maid:Mark(self.VForce)
2850
+ self._maid:Mark(self.FloorDetector)
2851
+ self._maid:Mark(self.Gyro)
2852
+ self.Model.Name = "Collider"
2853
+ self.Model.Parent = self.Controller.Character
2854
+ end
2855
+
2856
+ -- Public Methods
2857
+
2858
+ function ColliderClass:Update(force, cframe)
2859
+ self.VForce.Force = force
2860
+ self.Gyro.CFrame = cframe
2861
+ end
2862
+
2863
+ function ColliderClass:IsGrounded(isJumpCheck)
2864
+ local parts = (isJumpCheck and self.JumpDetector or self.FloorDetector):GetTouchingParts()
2865
+ for _, part in pairs(parts) do
2866
+ if not part:IsDescendantOf(self.Controller.Character) and part.CanCollide then
2867
+ return true
2868
+ end
2869
+ end
2870
+ end
2871
+
2872
+ function ColliderClass:GetStandingPart()
2873
+ params2.FilterDescendantsInstances = {self.Controller.Character}
2874
+
2875
+ local gravityUp = self.Controller._gravityUp
2876
+ local result = workspace:Raycast(self.Sphere.Position, -1.1*gravityUp, params2)
2877
+
2878
+ return result and result.Instance
2879
+ end
2880
+
2881
+ function ColliderClass:Destroy()
2882
+ self._maid:Sweep()
2883
+ end
2884
+
2885
+ --
2886
+
2887
+ return ColliderClass
2888
+ ]]></string>
2889
+ <int64 name="SourceAssetId">-1</int64>
2890
+ <BinaryString name="Tags"></BinaryString>
2891
+ </Properties>
2892
+ </Item>
2893
+ <Item class="ModuleScript" referent="20">
2894
+ <Properties>
2895
+ <string name="Name">StateTracker</string>
2896
+ <BinaryString name="AttributesSerialize"></BinaryString>
2897
+ <bool name="DefinesCapabilities">false</bool>
2898
+ <Content name="LinkedSource">
2899
+ <null>
2900
+ </null>
2901
+ </Content>
2902
+ <string name="Source">local Maid = require(script.Parent.Utility.Maid)
2903
+ local Signal = require(script.Parent.Utility.Signal)
2904
+
2905
+ -- CONSTANTS
2906
+
2907
+ local SPEED = {
2908
+ [Enum.HumanoidStateType.Running] = true,
2909
+ }
2910
+
2911
+ local IN_AIR = {
2912
+ [Enum.HumanoidStateType.Jumping] = true,
2913
+ [Enum.HumanoidStateType.Freefall] = true
2914
+ }
2915
+
2916
+ local REMAP = {
2917
+ ["onFreefall"] = "onFreeFall",
2918
+ }
2919
+
2920
+ -- Class
2921
+
2922
+ local StateTrackerClass = {}
2923
+ StateTrackerClass.__index = StateTrackerClass
2924
+ StateTrackerClass.ClassName = "StateTracker"
2925
+
2926
+ -- Public Constructors
2927
+
2928
+ function StateTrackerClass.new(controller)
2929
+ local self = setmetatable({}, StateTrackerClass)
2930
+
2931
+ self._maid = Maid.new()
2932
+
2933
+ self.Controller = controller
2934
+ self.State = Enum.HumanoidStateType.Running
2935
+ self.Speed = 0
2936
+
2937
+ self.Jumped = false
2938
+ self.JumpTick = os.clock()
2939
+
2940
+ self.Animation = require(controller.Character:WaitForChild("Animate"):WaitForChild("Controller"))
2941
+ self.Changed = Signal.new()
2942
+
2943
+ init(self)
2944
+
2945
+ return self
2946
+ end
2947
+
2948
+ -- Private Methods
2949
+
2950
+ function init(self)
2951
+ self._maid:Mark(self.Changed)
2952
+ self._maid:Mark(self.Changed:Connect(function(state, speed)
2953
+ local name = "on" .. state.Name
2954
+ local func = self.Animation[REMAP[name] or name]
2955
+ func(speed)
2956
+ end))
2957
+ end
2958
+
2959
+ -- Public Methods
2960
+
2961
+ function StateTrackerClass:Update(gravityUp, isGrounded, isInputMoving)
2962
+ local cVelocity = self.Controller.HRP.Velocity
2963
+ local gVelocity = cVelocity:Dot(gravityUp)
2964
+
2965
+ local oldState = self.State
2966
+ local oldSpeed = self.Speed
2967
+
2968
+ local newState = nil
2969
+ local newSpeed = cVelocity.Magnitude
2970
+
2971
+ if not isGrounded then
2972
+ if gVelocity > 0 then
2973
+ if self.Jumped then
2974
+ newState = Enum.HumanoidStateType.Jumping
2975
+ else
2976
+ newState = Enum.HumanoidStateType.Freefall
2977
+ end
2978
+ else
2979
+ if self.Jumped then
2980
+ self.Jumped = false
2981
+ end
2982
+ newState = Enum.HumanoidStateType.Freefall
2983
+ end
2984
+ else
2985
+ if self.Jumped and os.clock() - self.JumpTick > 0.1 then
2986
+ self.Jumped = false
2987
+ end
2988
+ newSpeed = (cVelocity - gVelocity*gravityUp).Magnitude
2989
+ newState = Enum.HumanoidStateType.Running
2990
+ end
2991
+
2992
+ newSpeed = isInputMoving and newSpeed or 0
2993
+
2994
+ if oldState ~= newState or (SPEED[newState] and math.abs(newSpeed - oldSpeed) > 0.1) then
2995
+ self.State = newState
2996
+ self.Speed = newSpeed
2997
+ self.Changed:Fire(newState, newSpeed)
2998
+ end
2999
+ end
3000
+
3001
+ function StateTrackerClass:RequestJump()
3002
+ self.Jumped = true
3003
+ self.JumpTick = os.clock()
3004
+ end
3005
+
3006
+ function StateTrackerClass:Destroy()
3007
+ self._maid:Sweep()
3008
+ end
3009
+
3010
+ return StateTrackerClass</string>
3011
+ <int64 name="SourceAssetId">-1</int64>
3012
+ <BinaryString name="Tags"></BinaryString>
3013
+ </Properties>
3014
+ </Item>
3015
+ <Item class="Folder" referent="21">
3016
+ <Properties>
3017
+ <string name="Name">Utility</string>
3018
+ <BinaryString name="AttributesSerialize"></BinaryString>
3019
+ <bool name="DefinesCapabilities">false</bool>
3020
+ <int64 name="SourceAssetId">-1</int64>
3021
+ <BinaryString name="Tags"></BinaryString>
3022
+ </Properties>
3023
+ <Item class="ModuleScript" referent="22">
3024
+ <Properties>
3025
+ <string name="Name">Maid</string>
3026
+ <BinaryString name="AttributesSerialize"></BinaryString>
3027
+ <bool name="DefinesCapabilities">false</bool>
3028
+ <Content name="LinkedSource">
3029
+ <null>
3030
+ </null>
3031
+ </Content>
3032
+ <string name="Source">-- CONSTANTS
3033
+
3034
+ local FORMAT_STR = "Maid does not support type \"%s\""
3035
+
3036
+ local DESTRUCTORS = {
3037
+ ["function"] = function(item)
3038
+ item()
3039
+ end;
3040
+ ["RBXScriptConnection"] = function(item)
3041
+ item:Disconnect()
3042
+ end;
3043
+ ["Instance"] = function(item)
3044
+ item:Destroy()
3045
+ end;
3046
+ ["table"] = function(item)
3047
+ item:Destroy()
3048
+ end
3049
+ }
3050
+
3051
+ -- Class
3052
+
3053
+ local MaidClass = {}
3054
+ MaidClass.__index = MaidClass
3055
+ MaidClass.ClassName = "Maid"
3056
+
3057
+ -- Public Constructors
3058
+
3059
+ function MaidClass.new(...)
3060
+ local self = setmetatable({}, MaidClass)
3061
+
3062
+ self.Trash = {}
3063
+
3064
+ for _, item in pairs({...}) do
3065
+ self:Mark(item)
3066
+ end
3067
+
3068
+ return self
3069
+ end
3070
+
3071
+ -- Public Methods
3072
+
3073
+ function MaidClass:Mark(item)
3074
+ local tof = typeof(item)
3075
+
3076
+ if DESTRUCTORS[tof] then
3077
+ self.Trash[item] = tof
3078
+ else
3079
+ error(FORMAT_STR:format(tof), 2)
3080
+ end
3081
+ end
3082
+
3083
+ function MaidClass:Unmark(item)
3084
+ if item then
3085
+ self.Trash[item] = nil
3086
+ else
3087
+ self.Trash = {}
3088
+ end
3089
+ end
3090
+
3091
+ function MaidClass:Sweep()
3092
+ for item, tof in pairs(self.Trash) do
3093
+ DESTRUCTORS[tof](item)
3094
+ end
3095
+ self.Trash = {}
3096
+ end
3097
+
3098
+ MaidClass.Destroy = MaidClass.Sweep
3099
+
3100
+ --
3101
+
3102
+ return MaidClass</string>
3103
+ <int64 name="SourceAssetId">-1</int64>
3104
+ <BinaryString name="Tags"></BinaryString>
3105
+ </Properties>
3106
+ </Item>
3107
+ <Item class="ModuleScript" referent="23">
3108
+ <Properties>
3109
+ <string name="Name">Signal</string>
3110
+ <BinaryString name="AttributesSerialize"></BinaryString>
3111
+ <bool name="DefinesCapabilities">false</bool>
3112
+ <Content name="LinkedSource">
3113
+ <null>
3114
+ </null>
3115
+ </Content>
3116
+ <string name="Source">-- Taken from Quenty's nevermore engine
3117
+ -- https://github.com/Quenty/NevermoreEngine/blob/version2/LICENSE.md
3118
+ -- https://github.com/Quenty/NevermoreEngine
3119
+
3120
+ --- Lua-side duplication of the API of events on Roblox objects.
3121
+ -- Signals are needed for to ensure that for local events objects are passed by
3122
+ -- reference rather than by value where possible, as the BindableEvent objects
3123
+ -- always pass signal arguments by value, meaning tables will be deep copied.
3124
+ -- Roblox's deep copy method parses to a non-lua table compatable format.
3125
+ -- @classmod Signal
3126
+
3127
+ local ENABLE_TRACEBACK = false
3128
+
3129
+ local Signal = {}
3130
+ Signal.__index = Signal
3131
+ Signal.ClassName = "Signal"
3132
+
3133
+ --- Constructs a new signal.
3134
+ -- @constructor Signal.new()
3135
+ -- @treturn Signal
3136
+ function Signal.new()
3137
+ local self = setmetatable({}, Signal)
3138
+
3139
+ self._bindableEvent = Instance.new("BindableEvent")
3140
+ self._argData = nil
3141
+ self._argCount = nil -- Prevent edge case of :Fire("A", nil) --> "A" instead of "A", nil
3142
+
3143
+ self._source = ENABLE_TRACEBACK and debug.traceback() or ""
3144
+
3145
+ return self
3146
+ end
3147
+
3148
+ --- Fire the event with the given arguments. All handlers will be invoked. Handlers follow
3149
+ -- Roblox signal conventions.
3150
+ -- @param ... Variable arguments to pass to handler
3151
+ -- @treturn nil
3152
+ function Signal:Fire(...)
3153
+ if not self._bindableEvent then
3154
+ warn(("Signal is already destroyed. %s"):format(self._source))
3155
+ return
3156
+ end
3157
+
3158
+ self._argData = {...}
3159
+ self._argCount = select("#", ...)
3160
+ self._bindableEvent:Fire()
3161
+ -- self._argData = nil
3162
+ -- self._argCount = nil
3163
+ end
3164
+
3165
+ --- Connect a new handler to the event. Returns a connection object that can be disconnected.
3166
+ -- @tparam function handler Function handler called with arguments passed when `:Fire(...)` is called
3167
+ -- @treturn Connection Connection object that can be disconnected
3168
+ function Signal:Connect(handler)
3169
+ if not (type(handler) == "function") then
3170
+ error(("connect(%s)"):format(typeof(handler)), 2)
3171
+ end
3172
+
3173
+ return self._bindableEvent.Event:Connect(function()
3174
+ handler(unpack(self._argData, 1, self._argCount))
3175
+ end)
3176
+ end
3177
+
3178
+ --- Wait for fire to be called, and return the arguments it was given.
3179
+ -- @treturn ... Variable arguments from connection
3180
+ function Signal:Wait()
3181
+ self._bindableEvent.Event:Wait()
3182
+ assert(self._argData, "Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.")
3183
+ return unpack(self._argData, 1, self._argCount)
3184
+ end
3185
+
3186
+ --- Disconnects all connected events to the signal. Voids the signal as unusable.
3187
+ -- @treturn nil
3188
+ function Signal:Destroy()
3189
+ if self._bindableEvent then
3190
+ self._bindableEvent:Destroy()
3191
+ self._bindableEvent = nil
3192
+ end
3193
+
3194
+ self._argData = nil
3195
+ self._argCount = nil
3196
+
3197
+ setmetatable(self, nil)
3198
+ end
3199
+
3200
+ return Signal</string>
3201
+ <int64 name="SourceAssetId">-1</int64>
3202
+ <BinaryString name="Tags"></BinaryString>
3203
+ </Properties>
3204
+ </Item>
3205
+ </Item>
3206
+ </Item>
3207
+ </Item>
3208
+ </roblox>