@quenty/ragdoll 9.22.0 → 9.23.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [9.23.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ragdoll@9.22.0...@quenty/ragdoll@9.23.0) (2023-05-08)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Ragdoll initializes CameraStackService ([814f327](https://github.com/Quenty/NevermoreEngine/commit/814f327beb53ab3665428a6357ca4293a7fdd9be))
12
+ * Will not try to unragdoll automatically while falling ([ac3c062](https://github.com/Quenty/NevermoreEngine/commit/ac3c06200f6dd6a09ee53ed0f3a726bcc4a3105e))
13
+
14
+
15
+ ### Features
16
+
17
+ * Reduce friction torqueon ragdoll head ([cfd1535](https://github.com/Quenty/NevermoreEngine/commit/cfd153587f199da77edce185c49a957aa23aee6b))
18
+
19
+
20
+
21
+
22
+
6
23
  # [9.22.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ragdoll@9.21.0...@quenty/ragdoll@9.22.0) (2023-04-20)
7
24
 
8
25
  **Note:** Version bump only for package @quenty/ragdoll
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/ragdoll",
3
- "version": "9.22.0",
3
+ "version": "9.23.0",
4
4
  "description": "Quenty's Ragdoll system for Roblox - Floppy fun ragdolls",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -25,33 +25,34 @@
25
25
  "Quenty"
26
26
  ],
27
27
  "dependencies": {
28
- "@quenty/attributeutils": "^8.13.0",
28
+ "@quenty/attributeutils": "^8.14.0",
29
29
  "@quenty/baseobject": "^6.2.1",
30
- "@quenty/binder": "^8.14.0",
31
- "@quenty/brio": "^8.11.0",
32
- "@quenty/camera": "^9.11.0",
30
+ "@quenty/binder": "^8.15.0",
31
+ "@quenty/brio": "^8.12.0",
32
+ "@quenty/camera": "^9.12.0",
33
33
  "@quenty/cancellabledelay": "^3.4.0",
34
- "@quenty/characterutils": "^6.5.0",
34
+ "@quenty/characterutils": "^6.6.0",
35
35
  "@quenty/draw": "^4.2.0",
36
36
  "@quenty/enumutils": "^3.1.0",
37
37
  "@quenty/hapticfeedbackutils": "^3.1.0",
38
- "@quenty/instanceutils": "^7.12.0",
38
+ "@quenty/instanceutils": "^7.13.0",
39
39
  "@quenty/loader": "^6.2.1",
40
40
  "@quenty/maid": "^2.5.0",
41
- "@quenty/motor6d": "^1.20.0",
42
- "@quenty/playerhumanoidbinder": "^8.14.0",
41
+ "@quenty/motor6d": "^1.21.0",
42
+ "@quenty/playerhumanoidbinder": "^8.15.0",
43
43
  "@quenty/promise": "^6.5.0",
44
44
  "@quenty/qframe": "^6.6.0",
45
- "@quenty/r15utils": "^7.13.0",
45
+ "@quenty/r15utils": "^7.14.0",
46
46
  "@quenty/remoting": "^6.5.0",
47
47
  "@quenty/rx": "^7.10.0",
48
+ "@quenty/rxbinderutils": "^8.16.0",
48
49
  "@quenty/spring": "^6.2.1",
49
50
  "@quenty/steputils": "^3.2.0",
50
51
  "@quenty/table": "^3.2.0",
51
- "@quenty/valuebaseutils": "^7.12.0"
52
+ "@quenty/valuebaseutils": "^7.13.0"
52
53
  },
53
54
  "publishConfig": {
54
55
  "access": "public"
55
56
  },
56
- "gitHead": "15d1274fffdaef706f849fd5dca2f14364c1264e"
57
+ "gitHead": "2ad8cea7dd3ad79a39afd7d7b785b489b90553fd"
57
58
  }
@@ -19,6 +19,9 @@ local CharacterUtils = require("CharacterUtils")
19
19
  local HapticFeedbackUtils = require("HapticFeedbackUtils")
20
20
  local RagdollServiceClient = require("RagdollServiceClient")
21
21
  local RagdollMotorUtils = require("RagdollMotorUtils")
22
+ local RxR15Utils = require("RxR15Utils")
23
+ local RxBrioUtils = require("RxBrioUtils")
24
+ local RxInstanceUtils = require("RxInstanceUtils")
22
25
 
23
26
  local RagdollClient = setmetatable({}, BaseObject)
24
27
  RagdollClient.ClassName = "RagdollClient"
@@ -51,28 +54,58 @@ function RagdollClient.new(humanoid, serviceBag)
51
54
  return self
52
55
  end
53
56
 
54
- -- TODO: Move out of this open source module
55
57
  function RagdollClient:_setupCameraShake(impulseCamera)
56
- local head = self._obj.Parent:FindFirstChild("Head")
57
- if not head then
58
- return
59
- end
58
+ -- TODO: Move out of this open source module
59
+
60
+ -- Use the upper torso instead of the head because the upper torso shakes a lot less so
61
+ -- we get a stronger response to full character movement.
62
+
63
+ self._maid:GiveTask(RxInstanceUtils.observePropertyBrio(self._obj, "Parent", function(character)
64
+ return character ~= nil
65
+ end):Pipe({
66
+ RxBrioUtils.switchMapBrio(function(character)
67
+ return RxBrioUtils.flatCombineLatestBrio({
68
+ upperTorso = RxR15Utils.observeCharacterPartBrio(character, "UpperTorso");
69
+ head = RxR15Utils.observeCharacterPartBrio(character, "Head")
70
+ }, function(state)
71
+ return state.upperTorso and state.head
72
+ end)
73
+ end);
74
+ }):Subscribe(function(brio)
75
+ if brio:IsDead() then
76
+ return
77
+ end
60
78
 
61
- local lastVelocity = head.Velocity
62
- self._maid:GiveTask(RunService.Heartbeat:Connect(function()
63
- debug.profilebegin("ragdollcamerashake")
64
- local cameraCFrame = Workspace.CurrentCamera.CFrame
79
+ local maid = brio:ToMaid()
80
+ local state = brio:GetValue()
65
81
 
66
- local velocity = head.Velocity
67
- local dVelocity = velocity - lastVelocity
68
- if dVelocity.magnitude >= 0 then
69
- if self._ragdollServiceClient:GetScreenShakeEnabled() then
70
- impulseCamera:Impulse(cameraCFrame:vectorToObjectSpace(-0.1*cameraCFrame.lookVector:Cross(dVelocity)))
71
- end
82
+ local function getEstimatedVelocityFromUpperTorso()
83
+ -- TODO: Consider neck attachments
84
+ local headOffset = state.upperTorso.Size*Vector3.new(0, 0.5, 0)
85
+ + state.head.Size*Vector3.new(0, 0.5, 0)
86
+ local headPosition = state.upperTorso.CFrame:PointToWorldSpace(headOffset)
87
+
88
+ return state.upperTorso:GetVelocityAtPosition(headPosition)
72
89
  end
73
90
 
74
- lastVelocity = velocity
75
- debug.profileend()
91
+ local lastVelocity = getEstimatedVelocityFromUpperTorso()
92
+ maid:GiveTask(RunService.Heartbeat:Connect(function()
93
+
94
+ debug.profilebegin("ragdollcamerashake")
95
+
96
+ local cameraCFrame = Workspace.CurrentCamera.CFrame
97
+
98
+ local velocity = getEstimatedVelocityFromUpperTorso()
99
+ local dVelocity = velocity - lastVelocity
100
+ if dVelocity.magnitude >= 0 then
101
+ if self._ragdollServiceClient:GetScreenShakeEnabled() then
102
+ impulseCamera:Impulse(cameraCFrame:vectorToObjectSpace(-0.1*cameraCFrame.lookVector:Cross(dVelocity)))
103
+ end
104
+ end
105
+
106
+ lastVelocity = velocity
107
+ debug.profileend()
108
+ end))
76
109
  end))
77
110
  end
78
111
 
@@ -23,6 +23,7 @@ function RagdollServiceClient:Init(serviceBag)
23
23
 
24
24
  -- External
25
25
  self._serviceBag:GetService(require("Motor6DServiceClient"))
26
+ self._serviceBag:GetService(require("CameraStackService"))
26
27
 
27
28
  -- Internal
28
29
  self._serviceBag:GetService(require("RagdollBindersClient"))
@@ -43,10 +43,8 @@ function RagdollHumanoidOnFall.new(humanoid, serviceBag)
43
43
  self:_handleServerEvent(...)
44
44
  end))
45
45
  else
46
- self._ragdollLogic = BindableRagdollHumanoidOnFall.new(self._obj, self._ragdollBinder)
47
- self._maid:GiveTask(self._ragdollLogic)
48
46
 
49
- self._maid:GiveTask(self._ragdollLogic.ShouldRagdoll.Changed:Connect(function()
47
+ self._maid:GiveTask(self:_getOrCreateRagdollLogic().ShouldRagdoll.Changed:Connect(function()
50
48
  self:_update()
51
49
  end))
52
50
  end
@@ -54,6 +52,22 @@ function RagdollHumanoidOnFall.new(humanoid, serviceBag)
54
52
  return self
55
53
  end
56
54
 
55
+ function RagdollHumanoidOnFall:ObserveIsFalling()
56
+ -- TODO: Remove logic if nothing is observing it
57
+ return self:_getOrCreateRagdollLogic():ObserveIsFalling()
58
+ end
59
+
60
+ function RagdollHumanoidOnFall:_getOrCreateRagdollLogic()
61
+ if self._ragdollLogic then
62
+ return self._ragdollLogic
63
+ end
64
+
65
+ self._ragdollLogic = BindableRagdollHumanoidOnFall.new(self._obj, self._ragdollBinder)
66
+ self._maid:GiveTask(self._ragdollLogic)
67
+
68
+ return self._ragdollLogic
69
+ end
70
+
57
71
  function RagdollHumanoidOnFall:_handleServerEvent(player, value)
58
72
  assert(player == self._player, "Bad player")
59
73
  assert(typeof(value) == "boolean", "Bad value")
@@ -16,6 +16,8 @@ local cancellableDelay = require("cancellableDelay")
16
16
  local Observable = require("Observable")
17
17
  local Rx = require("Rx")
18
18
  local RxInstanceUtils = require("RxInstanceUtils")
19
+ local RxBrioUtils = require("RxBrioUtils")
20
+ local RxBinderUtils = require("RxBinderUtils")
19
21
 
20
22
  local UnragdollAutomatically = setmetatable({}, BaseObject)
21
23
  UnragdollAutomatically.ClassName = "UnragdollAutomatically"
@@ -83,14 +85,27 @@ function UnragdollAutomatically:_observeCanUnragdollTimer()
83
85
  isReady.Value = false
84
86
  maid:GiveTask(isReady)
85
87
 
86
- maid:GiveTask(Rx.combineLatest({
87
- enabled = self._unragdollAutomatically:Observe();
88
+ maid:GiveTask(RxBrioUtils.flatCombineLatest({
89
+ canUnragdoll = RxBrioUtils.flatCombineLatest({
90
+ enabled = self._unragdollAutomatically:Observe();
91
+ isFallingRagdoll = self:_observeIsFallingRagdoll();
92
+ }):Pipe({
93
+ Rx.map(function(state)
94
+ return state.enabled and not state.isFallingRagdoll
95
+ end);
96
+ Rx.distinct();
97
+ Rx.tap(function(canUnragdoll)
98
+ -- Ensure we reset timer if we change state
99
+ if canUnragdoll then
100
+ startTime = os.clock()
101
+ end
102
+ end);
103
+ });
88
104
  time = self._unragdollAutomaticTime:Observe();
89
105
  }):Subscribe(function(state)
90
- if state.enabled then
106
+ if state.canUnragdoll then
91
107
  maid._deferred = nil
92
108
 
93
-
94
109
  local timeElapsed = os.clock() - startTime
95
110
  local remainingTime = state.time - timeElapsed
96
111
  if remainingTime <= 0 then
@@ -102,6 +117,7 @@ function UnragdollAutomatically:_observeCanUnragdollTimer()
102
117
  end)
103
118
  end
104
119
  else
120
+
105
121
  isReady.Value = false
106
122
  maid._deferred = nil
107
123
  end
@@ -116,4 +132,15 @@ function UnragdollAutomatically:_observeCanUnragdollTimer()
116
132
  end)
117
133
  end
118
134
 
135
+ function UnragdollAutomatically:_observeIsFallingRagdoll()
136
+ return RxBinderUtils.observeBoundClassBrio(self._ragdollBindersServer.RagdollHumanoidOnFall, self._obj)
137
+ :Pipe({
138
+ RxBrioUtils.switchMapBrio(function(ragdollHumanoidOnFall)
139
+ return ragdollHumanoidOnFall:ObserveIsFalling()
140
+ end);
141
+ RxBrioUtils.emitOnDeath(false);
142
+ Rx.distinct();
143
+ })
144
+ end
145
+
119
146
  return UnragdollAutomatically
@@ -6,6 +6,7 @@
6
6
  local require = require(script.Parent.loader).load(script)
7
7
 
8
8
  local BaseObject = require("BaseObject")
9
+ local RxInstanceUtils = require("RxInstanceUtils")
9
10
 
10
11
  local FRAMES_TO_EXAMINE = 8
11
12
  local FRAME_TIME = 0.1
@@ -31,6 +32,10 @@ function BindableRagdollHumanoidOnFall.new(humanoid, ragdollBinder)
31
32
  self.ShouldRagdoll.Value = false
32
33
  self._maid:GiveTask(self.ShouldRagdoll)
33
34
 
35
+ self._isFalling = Instance.new("BoolValue")
36
+ self._isFalling.Value = false
37
+ self._maid:GiveTask(self._isFalling)
38
+
34
39
  -- Setup Ragdoll
35
40
  self:_initLastVelocityRecords()
36
41
  self._lastRagDollTime = 0
@@ -50,7 +55,7 @@ function BindableRagdollHumanoidOnFall.new(humanoid, ragdollBinder)
50
55
 
51
56
  self._maid:GiveTask(self._ragdollBinder:ObserveInstance(self._obj, function(class)
52
57
  if not class then
53
- self._lastRagDollTime = tick()
58
+ self._lastRagDollTime = os.clock()
54
59
  self.ShouldRagdoll.Value = false
55
60
  end
56
61
  end))
@@ -58,6 +63,10 @@ function BindableRagdollHumanoidOnFall.new(humanoid, ragdollBinder)
58
63
  return self
59
64
  end
60
65
 
66
+ function BindableRagdollHumanoidOnFall:ObserveIsFalling()
67
+ return RxInstanceUtils.observeProperty(self._isFalling, "Value")
68
+ end
69
+
61
70
  function BindableRagdollHumanoidOnFall:_initLastVelocityRecords()
62
71
  self._lastVelocityRecords = {}
63
72
  for _ = 1, FRAMES_TO_EXAMINE + 1 do -- Add an extra frame because we remove before inserting
@@ -103,6 +112,7 @@ function BindableRagdollHumanoidOnFall:_updateVelocity()
103
112
 
104
113
  local rootPart = self._obj.RootPart
105
114
  if not rootPart then
115
+ self._isFalling.Value = false
106
116
  table.insert(self._lastVelocityRecords, Vector3.new())
107
117
  return
108
118
  end
@@ -125,13 +135,18 @@ function BindableRagdollHumanoidOnFall:_updateVelocity()
125
135
  table.insert(self._lastVelocityRecords, currentVelocity)
126
136
 
127
137
  if not fellForAllFrames then
138
+ self._isFalling.Value = false
128
139
  return
129
140
  end
130
141
 
131
142
  if mostNegativeVelocityY >= REQUIRED_MAX_FALL_VELOCITY then
143
+ self._isFalling.Value = false
132
144
  return
133
145
  end
134
146
 
147
+ -- Write that we're falling (candidate for ragdoll)
148
+ self._isFalling.Value = true
149
+
135
150
  -- print("currentVelocity.magnitude, mostNegativeVelocityY", currentVelocity.magnitude, mostNegativeVelocityY)
136
151
 
137
152
  if self._obj.Health <= 0 then
@@ -148,7 +163,8 @@ function BindableRagdollHumanoidOnFall:_updateVelocity()
148
163
  return
149
164
  end
150
165
 
151
- if (tick() - self._lastRagDollTime) <= RAGDOLL_DEBOUNCE_TIME then
166
+
167
+ if (os.clock() - self._lastRagDollTime) <= RAGDOLL_DEBOUNCE_TIME then
152
168
  return
153
169
  end
154
170
 
@@ -26,7 +26,7 @@ local HEAD_LIMITS = {
26
26
  UpperAngle = 45,
27
27
  TwistLowerAngle = -40,
28
28
  TwistUpperAngle = 40,
29
- FrictionTorque = 400,
29
+ FrictionTorque = 15, -- 400
30
30
  ReferenceMass = 1.0249234437943,
31
31
  }
32
32