1
- -- Rope Physics Module
2
- -- Ultra-rigid Verlet rope physics with pure position-based constraints
3
- -- No dampening, no force accumulation, no direct player manipulation
4
- -- EXTREMELY HARD TO BREAK: Rope only breaks at 500% stretch (5x original length)
1
+ -- filepath: /home/cretin/git/Cortex-Command-Community-Project/Data/Base.rte/Devices/Tools/GrappleGun/Scripts/RopePhysics.lua
2
+ --[[
3
+ RopePhysics.lua - Advanced Rope Physics Module
4
+
5
+ Implementation of ultra-rigid Verlet rope physics with pure position-based constraints.
6
+ Features:
7
+ - No dampening for realistic rope behavior
8
+ - No force accumulation, avoiding instability
9
+ - No direct player manipulation, only physics-based interactions
10
+ - Extremely durable: rope only breaks at 500% stretch (5x original length)
11
+ - Optimized for performance in high-stress situations
12
+
13
+ This module handles all rope physics calculations for the grappling hook,
14
+ including collision detection, tension forces, and segment constraints.
15
+ --]]
5
16
6
17
local RopePhysics = {}
7
18
8
- -- Verlet collision resolution (optimized for many segments)
19
+ --[[
20
+ Verlet collision resolution (optimized for many segments)
21
+ @param self The grapple instance
22
+ @param h The index of the segment to process
23
+ @param nextX The X component of the movement vector
24
+ @param nextY The Y component of the movement vector
25
+ ]]
9
26
function RopePhysics .verletCollide (self , h , nextX , nextY )
10
- -- APPLY FRICTION TO INDIVIDUAL JOINTS
27
+ -- Apply friction to individual joints
11
28
local ray = Vector (nextX , nextY )
12
29
local startpos = Vector (self .apx [h ], self .apy [h ])
13
- local rayvec = Vector ()
14
- local rayvec2 = Vector () -- This will store the surface normal
30
+ local rayvec = Vector () -- Will store collision point
31
+ local rayvec2 = Vector () -- Will store the surface normal
15
32
16
33
-- Skip collision check for very short movements to optimize performance
17
- if ray :MagnitudeIsLessThan (0.05 ) then -- Further reduced threshold
34
+ if ray :MagnitudeIsLessThan (0.05 ) then -- Performance optimization threshold
18
35
self .apx [h ] = self .apx [h ] + nextX
19
36
self .apy [h ] = self .apy [h ] + nextY
20
37
return
21
38
end
22
39
23
- rayl = SceneMan :CastObstacleRay (startpos , ray , rayvec , rayvec2 , (self .parent and self .parent .ID or 0 ), self .Team , rte .airID , 0 )
40
+ -- Cast ray to detect terrain and objects, using the parent entity ID to avoid self-collision
41
+ local rayl = SceneMan :CastObstacleRay (startpos , ray , rayvec , rayvec2 ,
42
+ (self .parent and self .parent .ID or 0 ),
43
+ self .Team , rte .airID , 0 )
24
44
25
45
if type (rayl ) == " number" and rayl >= 0 then
26
46
-- Collision detected at rayvec
@@ -63,6 +83,10 @@ function RopePhysics.verletCollide(self, h, nextX, nextY)
63
83
end
64
84
65
85
-- Calculate optimal segment count for a given rope length
86
+ -- Dynamically adjusts segments based on rope length for performance optimization
87
+ -- @param self The grapple instance
88
+ -- @param ropeLength The current length of the rope
89
+ -- @return The optimal number of segments for this rope length
66
90
function RopePhysics .calculateOptimalSegments (self , ropeLength )
67
91
-- Base calculation
68
92
local baseSegments = math.ceil (ropeLength / self .segmentLength )
@@ -80,18 +104,23 @@ function RopePhysics.calculateOptimalSegments(self, ropeLength)
80
104
end
81
105
82
106
-- Determine appropriate physics iterations based on segment count and distance
107
+ -- @param self The grapple instance
108
+ -- @return The number of physics iterations to use for this rope
83
109
function RopePhysics .optimizePhysicsIterations (self )
84
- -- Base iteration count
110
+ -- Base iteration count - balance between performance and physics accuracy
85
111
if self .currentSegments < 15 and self .currentLineLength < 150 then
86
- return 5 -- More iterations for shorter, more active ropes
112
+ return 36 -- More iterations for shorter, more active ropes (higher accuracy)
87
113
elseif self .currentSegments > 30 or self .currentLineLength > 300 then
88
- return 3 -- Fewer for very long ropes to save performance
114
+ return 9 -- Fewer iterations for very long ropes to save performance
89
115
end
90
116
91
- return 4 -- Default
117
+ return 18 -- Default for medium-length ropes
92
118
end
93
119
94
120
-- Resize the rope segments (add/remove/reposition)
121
+ -- Handles interpolation between previous and new segment counts
122
+ -- @param self The grapple instance
123
+ -- @param segments The new number of segments to use
95
124
function RopePhysics .resizeRopeSegments (self , segments )
96
125
-- Get current positions to interpolate from
97
126
local startPos = self .parent and self .parent .Pos or Vector (self .apx [0 ] or self .Pos .X , self .apy [0 ] or self .Pos .Y )
@@ -145,6 +174,8 @@ function RopePhysics.resizeRopeSegments(self, segments)
145
174
end
146
175
147
176
-- Update rope segments to form a straight line during flight
177
+ -- Used when the grappling hook is in flight and needs a clean path
178
+ -- @param self The grapple instance
148
179
function RopePhysics .updateRopeFlightPath (self )
149
180
if not (self .parent and self .apx and self .currentSegments > 0 ) then
150
181
return
@@ -183,6 +214,11 @@ function RopePhysics.updateRopeFlightPath(self)
183
214
end
184
215
185
216
-- Update the rope physics using Verlet integration with no dampening
217
+ -- Core physics update that handles all rope segment movement
218
+ -- @param grappleInstance The grapple instance to update
219
+ -- @param startPos Position vector of the start anchor (player)
220
+ -- @param endPos Position vector of the end anchor (hook)
221
+ -- @param cablelength Current maximum length of the cable
186
222
function RopePhysics .updateRopePhysics (grappleInstance , startPos , endPos , cablelength )
187
223
local segments = grappleInstance .currentSegments
188
224
if segments < 1 then return end
@@ -266,6 +302,11 @@ function RopePhysics.updateRopePhysics(grappleInstance, startPos, endPos, cablel
266
302
end
267
303
268
304
-- Advanced force protection system for preventing actor death from rope forces
305
+ -- Limits maximum force applied to actors to prevent unexpected deaths
306
+ -- @param grappleInstance The grapple instance
307
+ -- @param force_magnitude The original magnitude of the force
308
+ -- @param force_direction The direction vector of the force
309
+ -- @return The safe force magnitude and safe force vector
269
310
function RopePhysics .calculateActorProtection (grappleInstance , force_magnitude , force_direction )
270
311
-- Simplified - just return reduced values, no complex calculations
271
312
local safe_force_magnitude = math.min (force_magnitude , 5.0 ) -- Hard cap at 5
@@ -274,13 +315,17 @@ function RopePhysics.calculateActorProtection(grappleInstance, force_magnitude,
274
315
end
275
316
276
317
-- Apply stored forces gradually over time
318
+ -- Currently disabled in pure Verlet implementation
319
+ -- @param grappleInstance The grapple instance
277
320
function RopePhysics .applyStoredForces (grappleInstance )
278
321
-- Disabled - no stored forces in pure Verlet implementation
279
322
end
280
323
281
324
-- Proper Verlet rope constraint satisfaction with rigid distance constraints
282
325
-- Implements true non-stretchy rope behavior using position-based dynamics
283
- -- Now properly integrated with centralized length control
326
+ -- @param grappleInstance The grapple instance to apply constraints to
327
+ -- @param currentTotalCableLength The current total cable length
328
+ -- @return true if rope should break, false otherwise
284
329
function RopePhysics .applyRopeConstraints (grappleInstance , currentTotalCableLength )
285
330
local segments = grappleInstance .currentSegments
286
331
if segments == 0 then return false end
@@ -501,6 +546,9 @@ function RopePhysics.applyRopeConstraints(grappleInstance, currentTotalCableLeng
501
546
end
502
547
503
548
-- Smooth the rope using weighted averaging to reduce jaggedness
549
+ -- Applies limited averaging to intermediate points to create a smoother visual appearance
550
+ -- without significantly affecting the physics behavior
551
+ -- @param grappleInstance The grapple instance to smooth
504
552
function RopePhysics .smoothRope (grappleInstance )
505
553
local segments = grappleInstance .currentSegments
506
554
if segments < 3 then return end
@@ -535,6 +583,10 @@ function RopePhysics.smoothRope(grappleInstance)
535
583
end
536
584
537
585
-- Handle player pulling on the rope (manual or automatic)
586
+ -- Manages player interactions with the rope when pulling
587
+ -- @param grappleInstance The grapple instance
588
+ -- @param controller The input controller (optional)
589
+ -- @param terrCheck Flag to check terrain (optional)
538
590
function RopePhysics .handleRopePull (grappleInstance , controller , terrCheck )
539
591
local player = grappleInstance .parent
540
592
local segments = grappleInstance .currentSegments
@@ -552,6 +604,8 @@ function RopePhysics.handleRopePull(grappleInstance, controller, terrCheck)
552
604
end
553
605
554
606
-- Handle player extending the rope
607
+ -- Manages player interactions with the rope when extending
608
+ -- @param grappleInstance The grapple instance
555
609
function RopePhysics .handleRopeExtend (grappleInstance )
556
610
if grappleInstance .currentLineLength < grappleInstance .maxLineLength then
557
611
-- Placeholder for rope extension logic
@@ -560,6 +614,10 @@ function RopePhysics.handleRopeExtend(grappleInstance)
560
614
end
561
615
end
562
616
617
+ -- Check if the rope should break due to extreme tension
618
+ -- Uses simplified tension calculation based on segment stretching
619
+ -- @param grappleInstance The grapple instance
620
+ -- @return Sets grappleInstance.shouldBreak = true if rope should break
563
621
function RopePhysics .checkRopeBreak (grappleInstance )
564
622
-- Calculate tension (simplified)
565
623
local tension = 0
@@ -577,4 +635,6 @@ function RopePhysics.checkRopeBreak(grappleInstance)
577
635
end
578
636
end
579
637
638
+ -- Return the module for inclusion in other files
639
+ -- This module can be imported using: local RopePhysics = require("Base.rte/Devices/Tools/GrappleGun/Scripts/RopePhysics")
580
640
return RopePhysics
0 commit comments