@@ -107,13 +107,14 @@ end
107
107
-- @return The number of physics iterations to use for this rope
108
108
function RopePhysics .optimizePhysicsIterations (self )
109
109
-- Base iteration count - balance between performance and physics accuracy
110
- if self .currentSegments < 15 and self .currentLineLength < 150 then
111
- return 36 -- More iterations for shorter, more active ropes (higher accuracy)
112
- elseif self .currentSegments > 30 or self .currentLineLength > 300 then
113
- return 9 -- Fewer iterations for very long ropes to save performance
114
- end
115
-
116
- return 18 -- Default for medium-length ropes
110
+ -- if self.currentSegments < 15 and self.currentLineLength < 150 then
111
+ -- return 36 -- More iterations for shorter, more active ropes (higher accuracy)
112
+ -- elseif self.currentSegments > 30 or self.currentLineLength > 300 then
113
+ -- return 9 -- Fewer iterations for very long ropes to save performance
114
+ -- end
115
+ --
116
+ -- return 18 -- Default for medium-length ropes
117
+ return 32 -- User request: Set all iterations to 32
117
118
end
118
119
119
120
-- Resize the rope segments (add/remove/reposition)
@@ -331,103 +332,78 @@ function RopePhysics.applyRopeConstraints(grappleInstance, currentTotalCableLeng
331
332
332
333
if not grappleInstance .parent then return false end
333
334
334
- -- Use the centrally controlled rope length as the maximum constraint
335
335
local maxRopeLength = grappleInstance .currentLineLength or grappleInstance .maxLineLength
336
336
337
- -- FIRST: Enforce rigid maximum distance constraint through rope physics only
338
- -- Pure Verlet implementation - no direct player position manipulation
339
- local playerPos = grappleInstance .parent .Pos
337
+ local playerPos = grappleInstance .parent .Pos
340
338
local hookPos = Vector (grappleInstance .apx [segments ], grappleInstance .apy [segments ])
341
- local ropeVector = SceneMan :ShortestDistance (playerPos , hookPos , grappleInstance .mapWrapsX )
342
- local totalRopeDistance = ropeVector .Magnitude
343
339
344
- -- Update anchor positions for constraint calculations
340
+ -- Ensure player anchor point is up-to-date for constraint calculations
345
341
grappleInstance .apx [0 ] = playerPos .X
346
342
grappleInstance .apy [0 ] = playerPos .Y
347
343
348
- -- GLOBAL CONSTRAINT: Handle rope length constraints smoothly
344
+ local ropeVector = SceneMan :ShortestDistance (playerPos , hookPos , grappleInstance .mapWrapsX )
345
+ local totalRopeDistance = ropeVector .Magnitude
346
+
347
+ -- GLOBAL CONSTRAINT: Handle rope length limits
349
348
if totalRopeDistance > maxRopeLength then
350
349
local excessDistance = totalRopeDistance - maxRopeLength
351
- local constraintDirection = ropeVector :SetMagnitude (1 )
352
-
353
- -- Check if hook is anchored (attached to terrain or MO)
354
- -- if grappleInstance.actionMode >= 2 then -- Original condition
355
- if grappleInstance .actionMode == 2 then -- Changed: Actor anchored to claw only in mode 2
356
- -- Hook is anchored - apply PROPER SWINGING CONSTRAINT
357
- -- This allows free tangential movement (swinging) while constraining radial movement
350
+ local constraintDirection = ropeVector :SetMagnitude (1 ) -- Vector from player to hook
351
+
352
+ if grappleInstance .actionMode == 2 then -- Hook is anchored to terrain; player swings.
353
+ -- Player is overstretched. Correct position and velocity for a rigid swing.
358
354
359
- local currentVelocity = grappleInstance .parent .Vel
355
+ -- 1. Correct Player Position: Snap player precisely to the maxRopeLength arc.
356
+ local vec_from_hook_to_player = playerPos - hookPos -- Vector from hook to current player position
357
+ grappleInstance .parent .Pos = hookPos + vec_from_hook_to_player :SetMagnitude (maxRopeLength )
358
+
359
+ -- Update player's rope anchor point and local playerPos variable to reflect the correction.
360
+ grappleInstance .apx [0 ] = grappleInstance .parent .Pos .X
361
+ grappleInstance .apy [0 ] = grappleInstance .parent .Pos .Y
362
+ playerPos = grappleInstance .parent .Pos
363
+
364
+ -- 2. Correct Player Velocity: Make it purely tangential to the swing arc.
365
+ local currentVel = grappleInstance .parent .Vel
366
+ -- Define rope direction from the *newly corrected* player position to the hook.
367
+ local ropeDirFromPlayerToHook = (hookPos - playerPos ):SetMagnitude (1 )
360
368
361
- -- Calculate velocity component toward/away from hook
362
- -- constraintDirection points FROM player TO hook
363
- local radialVelocity = currentVelocity :Dot (constraintDirection )
369
+ local radialVelScalar = currentVel :Dot (ropeDirFromPlayerToHook )
370
+ -- radialVelScalar is the component of currentVel along the rope direction (player to hook).
371
+ -- If > 0, moving towards hook. If < 0, moving away from hook.
372
+ -- For a rigid tether at max length, all velocity along the rope axis should be nullified.
373
+ local radialVelocityVector = ropeDirFromPlayerToHook * radialVelScalar
374
+ local tangentialVelocity = currentVel - radialVelocityVector
364
375
365
- -- Only constrain the radial component if moving away from hook (stretching rope)
366
- if radialVelocity < 0 then
367
- -- Player is moving away from hook - remove ONLY the radial component
368
- -- Keep all tangential velocity for swinging motion
369
- local radialVelocityVector = constraintDirection * radialVelocity
370
- local tangentialVelocity = currentVelocity - radialVelocityVector
371
-
372
- -- Set velocity to pure tangential motion (perfect swinging)
373
- grappleInstance .parent .Vel = tangentialVelocity
374
-
375
- -- Set tension for physics feedback
376
- grappleInstance .ropeTensionForce = - radialVelocity * 0.5
377
- grappleInstance .ropeTensionDirection = constraintDirection
376
+ grappleInstance .parent .Vel = tangentialVelocity
377
+
378
+ -- Tension feedback: Indicate that the rope resisted outward motion.
379
+ -- resisted_outgoing_speed will be positive if player was moving away from hook.
380
+ local resisted_outgoing_speed = - radialVelScalar
381
+ if resisted_outgoing_speed > 0.01 then
382
+ grappleInstance .ropeTensionForce = resisted_outgoing_speed * 0.5 -- Magnitude based on resisted speed
383
+ grappleInstance .ropeTensionDirection = ropeDirFromPlayerToHook -- Force on player is towards hook
378
384
else
379
- -- Player is moving toward hook or tangentially - no constraint needed
380
- -- This allows free movement inward and pure swinging motion
381
385
grappleInstance .ropeTensionForce = nil
382
386
grappleInstance .ropeTensionDirection = nil
383
387
end
384
-
385
- -- CRITICAL: Also enforce position constraint to prevent gradual stretching
386
- -- After constraining velocity, ensure player doesn't drift beyond max rope length
387
- if totalRopeDistance > maxRopeLength then
388
- local correctionDistance = totalRopeDistance - maxRopeLength
389
- local correctionVector = constraintDirection * correctionDistance
390
-
391
- -- Move player back to exact rope radius (smooth correction)
392
- local correctionStrength = 0.8 -- Strong but not instant correction
393
- grappleInstance .parent .Pos = grappleInstance .parent .Pos + correctionVector * correctionStrength
394
-
395
- -- Update rope anchor to match corrected player position
396
- grappleInstance .apx [0 ] = grappleInstance .parent .Pos .X
397
- grappleInstance .apy [0 ] = grappleInstance .parent .Pos .Y
398
- end
399
- elseif grappleInstance .actionMode == 1 then -- Added: Claw anchored to actor in mode 1
400
- -- Hook is in flight, anchor it to the player
401
- local correctionVector = constraintDirection * excessDistance
402
- -- Move the player instead of the hook
403
- grappleInstance .parent .Pos = grappleInstance .parent .Pos + correctionVector
404
- -- Update rope anchor to match corrected player position
405
- grappleInstance .apx [0 ] = grappleInstance .parent .Pos .X
406
- grappleInstance .apy [0 ] = grappleInstance .parent .Pos .Y
407
-
408
- -- Clear any tension forces since rope is not under tension
409
- grappleInstance .ropeTensionForce = nil
410
- grappleInstance .ropeTensionDirection = nil
411
388
412
- -- Recalculate after constraint
413
- playerPos = grappleInstance .parent .Pos -- update playerPos for subsequent calculations
414
- ropeVector = SceneMan :ShortestDistance (playerPos , hookPos , grappleInstance .mapWrapsX )
415
- totalRopeDistance = ropeVector .Magnitude
416
- else
417
- -- Hook is in flight - we can move it to maintain rope length (default case)
418
- local correctionVector = constraintDirection * excessDistance
419
- grappleInstance .apx [segments ] = grappleInstance .apx [segments ] - correctionVector .X
389
+ else -- Handles actionMode == 1 (hook flying), actionMode == 3 (hook on MO), and any other defaults.
390
+ -- In these cases, the player is the anchor, and the hook end of the rope is corrected.
391
+ local correctionVector = constraintDirection * excessDistance -- constraintDirection is player -> hook
392
+
393
+ -- Move hook segment towards player
394
+ grappleInstance .apx [segments ] = grappleInstance .apx [segments ] - correctionVector .X
420
395
grappleInstance .apy [segments ] = grappleInstance .apy [segments ] - correctionVector .Y
421
396
422
- -- Clear any tension forces since rope is not under tension
423
- grappleInstance .ropeTensionForce = nil
397
+ grappleInstance .ropeTensionForce = nil
424
398
grappleInstance .ropeTensionDirection = nil
425
399
426
- -- Recalculate after constraint
427
- hookPos = Vector (grappleInstance .apx [segments ], grappleInstance .apy [segments ])
428
- ropeVector = SceneMan :ShortestDistance (playerPos , hookPos , grappleInstance .mapWrapsX )
429
- totalRopeDistance = ropeVector .Magnitude
400
+ hookPos = Vector (grappleInstance .apx [segments ], grappleInstance .apy [segments ]) -- Update hookPos for subsequent segment constraints
430
401
end
402
+
403
+ -- After any correction, update totalRopeDistance for the segment constraint part
404
+ -- This ensures the segment distribution logic uses the corrected overall length.
405
+ ropeVector = SceneMan :ShortestDistance (playerPos , hookPos , grappleInstance .mapWrapsX )
406
+ totalRopeDistance = ropeVector .Magnitude
431
407
else
432
408
-- Rope is not at maximum length - clear tension forces
433
409
grappleInstance .ropeTensionForce = nil
0 commit comments