Files
RowYourBoat/Scripts/RYBQuestScript.psc
Tyler Hallada c25499b41e prep v0.2.0
Set ModVersion and add update code. Update docs.

Might add another couple fixes before releasing 0.2.0.
2025-06-18 14:26:23 -04:00

1305 lines
61 KiB
Plaintext

ScriptName RYBQuestScript
float fQuestDelayTime ; controls script update rate, see: https://cs.uesp.net/wiki/FQuestDelayTime
float HighUpdateRate ; script updates per second when boat is moving or rocking (default: 0.0166)
float MediumUpdateRate ; script updates per second when boat is not moving or rocking but nearby (default: 0.1)
float LowUpdateRate ; script updates per second when far away from the boat (default: 1.0)
float ModVersion ; For handling updates
; Quest script states
short Initializing
; Boat states
short BoatMoving ; 0 = not moving, 1 = about to move this tick, 2 = in motion
short Rowing ; 0 = not rowing (timed), 1 = rowing forward (timed), 2 = rowing backward (timed)
short AutoRowing ; 0 = not auto rowing, 1 = auto rowing forward, 2 = auto rowing backward
; 1 = run positioning code once relative to player
; 2 = run positioning code once relative to boat
; -1 = re-enable boat and attachments, then run positioning code once relative to player
short Resetting
short LockHeading ; 0 = free to turn, 1 = locked to current boat heading
short LadderDeployed ; 0 = disabled, 1 = enabled
short LampOn
; 0 = not summoning, 1 = waiting for collider to move, 2 = moving boat to collider,
; 3 = moving slightly to fix collision/interaction issues
short Summoning
short Dragging ; 0 = not dragging, 1 = dragging
short Grounded ; 0 = not grounded, 1 = collided with something and stopped
short OnLand ; 0 = not on land, 1 = on land (Z position is too high) auto-set if BoatZ > LandZThreshold
short BoatPurchased
short ChestPurchased
short LampPurchased
short LadderPurchased
short PlayerNearBoat ; 0 = not near boat, 1 = near boat (within RockDistanceThreshold)
; Triggers
; Sort of like functions that any script can call by setting these to 1 which this script will handle and set back to 0
short TriggerAutoRow ; 1 = auto-row forward, 2 = auto-row backward
short TriggerGetOnBoat ; 1 = get on boat via SetPosition, 2 = sit on the seat
short TriggerStartDragging ; 1 = will start dragging if Dragging == 0
short TriggerStopDragging ; 1 = will stop dragging if Dragging == 1
short TriggerSummonBoat ; 1 = summon boat, 2 = place boat right here
short TriggerRowCast ; player casting the row spell will set this to 1 (forward) or 2 (back)
float SecondsPassed ; actual time passed since last frame (GetSecondsPassed)
float SmoothedDeltaTime ; SecondsPassed adjusted to the recent average frame rate using exponentially smoothing
; How much to smooth the delta time.
; Tune this between 0.05-0.2 to make adjustments more/less responsive to frame rate changes (default: 0.1)
float DeltaSmoothingFactor
float TargetDeltaTime ; target delta time to smooth to (default: 0.0166) aka. 60fps
; All "base" speed/rate values below assume a frame rate of 60fps (0.0166 seconds per frame)
; To prevent the boat from moving faster in higher FPS and slower in lower FPS, the speed values are dynamically
; adjusted to the current frame rate (based on the result of GetSecondsPassed exponentially smoothed over many frames).
float BaseBoatVelocity ; distance in units to move the boat every frame
float AbsoluteBoatVelocity ; absolute value of BaseBoatVelocity (used in calculating the turn rate)
float FrameBoatVelocity
float BoatMaxVelocity ; maximum distance in units to move the boat every frame (default: 6)
float VelocityDecayLnRetentionFactor ; natural log of the retention factor (0.99) per frame at 60fps (default: -0.01005)
float FrameVelocityDecay ; how much to multiply the velocity by every frame to decay it. will be calculated
float BaseRowForce ; how much to increase/decrease the velocity every frame when rowing (default: 0.05)
float FrameRowForce
float BaseTurnRate ; how much to turn the boat every frame (default: 0.4)
float TurnRateVelocityFactor ; how much to multiply the base turn rate by based on the boat's velocity (calculated)
float FrameTurnRate
float CurrentTurnRate ; the actual turn rate being applied this frame (decayed or accelerated version of FrameTurnRate)
float TurnRateAcceleration ; how quickly turn rate ramps up (default: 0.15)
float TurnRateDeceleration ; how quickly turn rate decays when not turning (default: 0.92)
float LandZThreshold ; Z position that the boat must be above to be considered on land (default: 10)
float WaterLevelZ ; Z position of the water level (default: 0)
float RowTimer ; current time left before stopping rowing
float RowSpellDuration ; duration to row after spell is cast (default: 1.0)
float OverboardDistance ; distance in units from the boat to be considered overboard (default: 300)
float PlayerDistance ; current distance from the player to the boat (returned from GetDistance)
float SummonDistance ; distance in units to place the boat in front of the player when summoning (default: 350)
; summoning the boat goes haywire unless we give things time to "settle" this delays moving the boat a bit
float SummonTimer ; how much time left to wait before executing the next step of summoning (default: 0.5)
float SummonTimerDelay ; how long to wait in seconds after summoning to move boat to collider (default: 0.5)
float PlayerSin
float PlayerCos
; Dragging physics variables
float DragRopeLength ; how far the player can get before boat starts moving (default: 100)
float DragTautDistanceSquared ; current distance from player to tow point (squared)
float DragSlackDistance ; how much slack is in the rope
float PlayerMovementDistance
float PlayerMovementForce
float DragPointX ; point where the rope connects (center of boat)
float DragPointY
float DragTargetAngle
float DragForceX ; force applied to boat
float DragForceY
float DragBoatVelocityX ; boat's current drag velocity
float DragBoatVelocityY
float DragVelocitySquared
float DragFriction ; how quickly boat stops when not being pulled. 1.0 = no friction (default: 0.85)
float DragBaseTurnRate ; how fast boat rotates to match drag direction (default: 0.1)
float DragFrameTurnRate ; how much to rotate the boat this frame adjusted for the current frame rate
float DragMaxTurnPerFrame ; maximum angle to turn the boat per frame while dragging (default: 0.1)
float DragTurnDeadzone ; difference of degrees in player angle to boat angle to ignore before turning (default: 5)
float DragCurrentTurnRate ; current turn rate applied this frame while dragging adjusted with accel/decel smoothing
float DragTurnRateAcceleration ; how quickly to accelerate the turn rate while dragging (default: 0.15)
float DragTurnRateDeceleration ; how quickly to decelerate the turn rate while dragging (default: 0.92)
float DragAngleDiff ; difference between boat angle and drag angle
float DragPullStrength ; how strong the pull is (default: 0.005)
float DragMaxForce ; maximum force to apply to the boat (default: 0.5)
float DragMaxVelocity ; maximum velocity of the boat while dragging (default: 4.0)
float DragZInterpolationRate ; how quickly to interpolate the Z position of the boat to the player (default: 0.1)
float DragPlayerBoatZDiff ; difference between player Z and boat Z
float DragFrameBoatZAdjust ; adjustment to the boat Z position this frame based on player Z
float DragLandPlayerZOffset ; offset from the player z to place the drag point, to raise the boat up (default: 30)
float DragMaxZPerFrame ; maximum Z distance to move the boat per frame while dragging (default: 0.1)
float DragMaxPlayerDistance ; maximum distance between player and boat before dragging is canceled (default: 1800)
float DragPitchAngle ; current smoothed pitch angle for dragging terrain
float DragTargetPitchAngle ; target pitch angle to smooth toward
float DragMaxPitchAngle ; maximum (absolute) pitch angle to apply while dragging (default: 45)
float DragPitchSmoothingFactor ; how quickly to smooth pitch changes (default: 0.15)
float DragPathStartX ; Start position of current path segment
float DragPathStartY
float DragPathStartZ
float DragPathDistance ; Distance traveled in current segment
float DragPathSlope ; Calculated slope of boat's path
float DragPathMinDistance ; Minimum distance before calculating slope (default: 50)
float DragPathSlopeFactor ; Factor to invert and minimize slope player path (default: -0.7)
float DragPathSlopeDeadzone ; Angle of player path that will not affect pitch (default: 2)
float DragTargetPitchMovingSmoothingFactor ; Additional smoothing factor on pitch applied while moving (default: 0.03)
float DragTargetPitchStoppedSmoothingFactor ; Additional smoothing factor on pitch applied while stopped (default: 0.02)
float DragUphillZAdjustmentFactor ; How much to adjust Z based on angle when going uphill (default: 6.0)
float DragDownhillZAdjustmentFactor ; How much to adjust Z based on angle when going downhill (default: 4.5)
; Boat drag angle calculation variables
float dX
float dY
float dXAbs
float dYAbs
float a
float r
float s
; Rocking animation variables
; IMPORTANT: Keep pitch and roll angles small (under 10-15 degrees) to avoid gimbal lock issues
; The boat's primary movement still relies on Z rotation, these are just visual effects
float RockingEnabled ; 0 = disabled, 1 = enabled (default: 1)
float RockPhase ; Current phase of the primary rocking motion (0-360)
float RockPhase2 ; Secondary rocking phase for more complex motion
float RockPhase3 ; Tertiary phase for roll motion
float RockAmplitudeZ ; How much the boat rocks up/down (in units) (default: 0.8)
float RockAmplitudePitch ; How much the boat pitches forward/back (in degrees) (default: 2.0)
float RockAmplitudeRoll ; How much the boat rolls side-to-side (in degrees) (default: 3.0)
float RockMaxAbsoluteZ ; Maximum BoatZ +/- Z offset value allowed from rocking (in units) (default: 3.0)
float RockFrequency ; How fast the boat rocks (degrees per second) (default: 30.0)
float RockFrequency2 ; Secondary frequency for complex motion (default: 45.0)
float RockFrequency3 ; Frequency for roll motion (default: 25.0)
float RockRandomPhase ; Random offset to make rocking look natural
float RockRandomTimer ; Timer for changing random values
float RockRandomInterval ; How often to change random values (in seconds) (default: 3.0)
float RockSpeedFactor ; How much boat speed affects rocking (default: 0.5)
float RockWeatherFactor ; Multiplier for bad weather (purely based on GetWindSpeed currently) (default: 0.5)
float WindSpeed ; Current wind speed (0-1) from GetWindSpeed
float RockDistanceThreshold ; Distance from player to enable rocking (in units) (default: 3000)
float TargetRockZOffset ; Target Z offset from rocking
float TargetRockPitchOffset ; Target pitch (X angle) offset from rocking
float TargetRockRollOffset ; Target roll (Y angle) offset from rocking
float RockZOffset ; Current smoothed boat Z offset from rocking
float RockPitchOffset ; Current smoothed pitch (X angle) offset from rocking
float RockRollOffset ; Current smoothed roll (Y angle) offset from rocking
float RockSmoothingFactor ; How quickly rocking starts from nothing and settles when stopped (default: 0.1)
float BoatPitchAngle ; Current pitch angle of boat
float BoatRollAngle ; Current roll angle of boat
; Sine approximation variables for rocking
float rockAng
float rockN
float rockT2
float rockSin
float rockSin2
float rockSin3
float rockCos3
; Attachment rotation calculation variables
float PitchRadians ; Pitch angle in radians for calculations
float RollRadians ; Roll angle in radians for calculations
float SinPitch ; Sine of pitch angle
float SinRoll ; Sine of roll angle
float CosPitch ; Cosine of pitch angle
float CosRoll ; Cosine of roll angle
float OrigX
float OrigY
float OrigZ
float TempX
float TempY
float TempZ
; Player weight influence variables
float PlayerWeightEnabled ; 0 = disabled, 1 = enabled (default: 1)
float PlayerLocalX ; Player's position relative to boat center (starboard/port)
float PlayerLocalY ; Player's position relative to boat center (bow/stern)
float PlayerLocalZ ; Player's position relative to boat center (up/down)
float PlayerRelativeX ; World relative position before rotation
float PlayerRelativeY ; World relative position before rotation
float PlayerDistanceFromCenter ; How far player is from boat center
float PlayerWeightInfluence ; 0-1 multiplier based on distance from center
float PlayerWeightMaxDistanceForward ; bow to stern max distance (default: 240)
float PlayerWeightMaxDistanceSide ; port to starboard max distance (default: 100)
float PlayerWeightMaxDistanceVertical ; vertical max distance (default: 60)
float PlayerWeightPitchFactor ; How much player position affects pitch (default: 0.15)
float PlayerWeightRollFactor ; How much player position affects roll (default: 0.12)
float PlayerWeightPitchOffset ; Pitch offset caused by player weight
float PlayerWeightRollOffset ; Roll offset caused by player weight
float PlayerWeightSmoothingFactor ; How quickly weight effects change (default: 0.2)
float TargetPlayerWeightPitchOffset ; Target pitch offset for smoothing
float TargetPlayerWeightRollOffset ; Target roll offset for smoothing
ref BoatRef
float BoatX
float BoatY
float BoatZ
float BoatZWithRock ; BoatZ + RockZOffset
float BoatAngle
float PlayerX
float PlayerY
float PlayerZ
float PlayerAngle
float Diff
float Hemi
float BoatModelAngle ; boat's in-game model is rotated 90 degrees offset from 0, this stores true model angle
float TurnDeadzone ; difference of degrees in player angle to boat angle to ignore before turning (default: 10)
; Math variables for boat movement phyiscs
float pi
float halfPi
float radToDeg
float degToRad
float ang
float n
float t2
; (specific to boat yaw angle)
float sin
float cos
float tan
ref BoatMarker
float BoatMarkerZ
float BoatMarkerZOffset ; offset from the boat to place the map marker (default: 20)
ref Seat
float SeatX
float SeatY
float SeatZ
float SeatForwardOffset ; pos units from boat center along bow to stern to place the seat
float SeatSideOffset ; pos units from boat center along port to starboard to place the seat
float SeatZOffset ; pos units from BoatZ upwards to place the seat (default: 19)
ref Chest
float ChestX
float ChestY
float ChestZ
float ChestAngle
float ChestAngleOffset
float ChestForwardOffset ; pos units from boat center along bow to stern to place the chest
float ChestSideOffset ; pos units from boat center along port to starboard to place the chest
float ChestZOffset ; pos units from BoatZ upwards to place the chest (default: 15)
ref LampLit
ref LampUnlit
float LampX
float LampY
float LampZ
float LampForwardOffset ; pos units from boat center along bow to stern to place the lamp
float LampSideOffset ; pos units from boat center along port to starboard to place the lamp
float LampZOffset ; pos units from BoatZ upwards to place the lamp (default: 87)
ref Ladder
float LadderX
float LadderY
float LadderZ
float LadderForwardOffset ; pos units from boat center along bow to stern to place the ladder
float LadderSideOffset ; pos units from boat center along port to starboard to place the ladder
float LadderZOffset ; pos units from BoatZ upwards to place the ladder (default: -28)
ref Collider
float ColliderX
float ColliderActualX
float ColliderY
float ColliderActualY
float ColliderZ
float ColliderOffset ; pos units from boat center towards boat direction to place the collider
float ColliderOffsetReverse ; pos units from boat center towards the back of the boat to place the collider
float ColliderMoveFreq ; how often to move the collider in seconds (default: 0.05)
float ColliderMoveTimer ; current time left to wait before moving the collider again
float CollisionDetectDelay ; how long to wait in seconds at boat startup before checking for collision (default: 2)
float CollisionDetectTimer ; current time left to give for moving the boat away at startup before detecting collision
short CollisionDetectZThreshold ; Z position that the collider must be above to be considered colliding with something
; how close (in units) the collider must be to the expected position to trigger a collision (default: 1.0)
float ColliderPosThreshold
begin GameMode
if (Initializing == 0)
set Initializing to 1
; start in low-processing mode and switch to faster later on when boat is moving
set fQuestDelayTime to LowUpdateRate
; Initialization is done in stage result scripts to save max script size space
SetStage RYB 1 ; init constants
SetStage RYB 2 ; init refs
SetStage RYB 3 ; init collider
SetStage RYB 4 ; init summoning
SetStage RYB 5 ; init dragging
SetStage RYB 6 ; init rocking
SetStage RYB 7 ; init player weight
endif
if (ModVersion < 0.2)
set ModVersion to 0.2
set ColliderPosThreshold to 1.0
endif
if (Resetting == -1)
SetStage RYB 51 ; Re-enable boat and attachment refs
set Resetting to 1
endif
set PlayerDistance to BoatRef.GetDistance Player
if (PlayerDistance < RockDistanceThreshold)
if (RockingEnabled == 1)
set fQuestDelayTime to HighUpdateRate ; high processing rate when near the boat for rocking animation
else
set fQuestDelayTime to MediumUpdateRate ; medium processing rate when nearby but rocking is disabled
endif
if (PlayerNearBoat == 0)
; Fix bug with boat disappearing after returning to the boat's cell by disabling all refs and re-enable in
; the next frame
SetStage RYB 50 ; Disable boat and attachment refs
set Resetting to -1 ; re-enable boat and attachment refs next frame
endif
set PlayerNearBoat to 1
else
set fQuestDelayTime to LowUpdateRate ; low processing rate when far away
set PlayerNearBoat to 0
; SetStage RYB 50 ; Disable boat and attachment refs when away
endif
if (TriggerAutoRow == 1)
set TriggerAutoRow to 0
set BoatMoving to 1
set fQuestDelayTime to HighUpdateRate
set AutoRowing to 1
set Grounded to 0
set DragTargetPitchAngle to 0
Seat.SetDestroyed 1
BoatMarker.Disable
Ladder.Disable
set LadderDeployed to 0
set BaseBoatVelocity to BaseRowForce
set CollisionDetectTimer to CollisionDetectDelay
elseif (TriggerAutoRow == 2)
set TriggerAutoRow to 0
set BoatMoving to 1
set Grounded to 0
set DragTargetPitchAngle to 0
set fQuestDelayTime to HighUpdateRate
set AutoRowing to 2
Seat.SetDestroyed 1
BoatMarker.Disable
Ladder.Disable
set LadderDeployed to 0
set BaseBoatVelocity to -BaseRowForce
set CollisionDetectTimer to CollisionDetectDelay
endif
; For some unknown reason, this player distance check only works in this script and not in the menu script
if (TriggerRowCast > 0 && PlayerDistance > OverboardDistance)
set TriggerRowCast to 0
set RYBMenu.TriggerRowCast to 3 ; not near boat messagebox
elseif (TriggerRowCast > 0 && Dragging >= 1)
set TriggerRowCast to 0
set RYBMenu.TriggerRowCast to 4 ; dragging boat messagebox
elseif (TriggerRowCast > 0 && OnLand == 1)
set TriggerRowCast to 0
set RYBMenu.TriggerRowCast to 5 ; boat on land messagebox
elseif (TriggerRowCast == 1)
set TriggerRowCast to 0
set AutoRowing to 0 ; rowing manually cancels auto rowing
set Rowing to 1
set RowTimer to RowSpellDuration
if (BoatMoving == 0)
set BoatMoving to 1
set Grounded to 0
set DragTargetPitchAngle to 0
set fQuestDelayTime to HighUpdateRate
Seat.SetDestroyed 1
BoatMarker.Disable
Ladder.Disable
set LadderDeployed to 0
set BaseBoatVelocity to BaseRowForce
set CollisionDetectTimer to CollisionDetectDelay
endif
elseif (TriggerRowCast == 2)
set TriggerRowCast to 0
set AutoRowing to 0 ; rowing manually cancels auto rowing
set DragTargetPitchAngle to 0
set Rowing to 2
set RowTimer to RowSpellDuration
if (BoatMoving == 0)
set BoatMoving to 1
set Grounded to 0
set fQuestDelayTime to HighUpdateRate
Seat.SetDestroyed 1
BoatMarker.Disable
Ladder.Disable
set LadderDeployed to 0
set BaseBoatVelocity to -BaseRowForce
set CollisionDetectTimer to CollisionDetectDelay
endif
endif
if (TriggerGetOnBoat == 1)
set TriggerGetOnBoat to 0
SetStage RYB 11 ; Update boat position and angle
set PlayerZ to BoatZ + 20
; TODO: this sometimes doesn't put the player on the boat
Player.SetPos x BoatX
Player.SetPos y BoatY
Player.SetPos z PlayerZ
Player.SetAngle z BoatAngle
Message "You climbed onto the boat."
elseif (TriggerGetOnBoat == 2)
set TriggerGetOnBoat to 0
Seat.Activate Player 1
endif
if (TriggerStartDragging == 1)
set TriggerStartDragging to 0
if (Dragging == 0)
set Dragging to 1
set DragTargetPitchAngle to 0 ; smooth reset to 0 pitch
Player.AddItem RYBBoatToken 1
set fQuestDelayTime to HighUpdateRate ; High update rate while dragging
Seat.SetDestroyed 1
BoatMarker.Disable
set Grounded to 0
; stop the boat from moving while summoning
set BaseBoatVelocity to 0
set CurrentTurnRate to 0
; Initialize dragging state
set DragBoatVelocityX to 0
set DragBoatVelocityY to 0
SetStage RYB 11 ; Update boat position and angle
set DragTargetAngle to BoatAngle
; Initialize path tracking
set DragPathStartX to Player.GetPos x
set DragPathStartY to Player.GetPos y
set DragPathStartZ to Player.GetPos z
set DragPathSlope to 0
if (OnLand == 1 && Player.IsSwimming == 0)
; When on land, raise the boat up a bit to indicate dragging has been activated
set DragPlayerBoatZDiff to (PlayerZ + DragLandPlayerZOffset) - BoatZ
set Dragging to 2 ; special state that allows z change until boat meets this z change
endif
Message "You are now dragging the boat."
endif
endif
if (TriggerStopDragging == 1)
set TriggerStopDragging to 0
if (Dragging >= 1)
set Dragging to 0
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
set DragBoatVelocityX to 0
set DragBoatVelocityY to 0
Player.RemoveItem RYBBoatToken 1
Seat.SetDestroyed 0
BoatMarker.Enable
Message "You stop dragging the boat."
endif
endif
if (TriggerSummonBoat >= 1)
set fQuestDelayTime to HighUpdateRate
; stop the boat from moving while summoning
set BoatMoving to 0
set Grounded to 0
Seat.SetDestroyed 0
set BaseBoatVelocity to 0
set CurrentTurnRate to 0
set DragTargetPitchAngle to 0
set PlayerX to Player.GetPos x
set PlayerY to Player.GetPos y
set PlayerZ to Player.GetPos z
if (PlayerZ < 1)
set PlayerZ to 1
endif
set PlayerAngle to Player.GetAngle z
if (PlayerAngle < 0)
set PlayerAngle to PlayerAngle + 360
elseif (PlayerAngle >= 360)
set PlayerAngle to PlayerAngle - 360
endif
set BoatAngle to PlayerAngle
set BoatModelAngle to BoatAngle + 90
if (BoatModelAngle < 0)
set BoatModelAngle to BoatModelAngle + 360
elseif (BoatModelAngle >= 360)
set BoatModelAngle to BoatModelAngle - 360
endif
; This positions the invisible collider SummonDistance units in front of the player.
; The game places actors in a way that avoids terrain and object collisions so this is a hack to prevent
; too much clipping on the boat when it is summoned.
set ang to PlayerAngle
SetStage RYB 18 ; Calculate sin/cos of the player angle
if (TriggerSummonBoat == 1)
set TriggerSummonBoat to 0
Message "Summoning boat..."
; Use collider to find a good spot to place the boat in front of the player
set ColliderX to PlayerX + (PlayerSin * SummonDistance)
set ColliderY to PlayerY + (PlayerCos * SummonDistance)
SetStage RYB 20 ; Enable and position collider
; need to wait for collider to move and "settle" before moving the boat to the spot the collider found
set SummonTimer to SummonTimerDelay
set Summoning to 1
return
elseif (TriggerSummonBoat == 2)
set TriggerSummonBoat to 0
Message "Placing boat..."
set BoatX to PlayerX + (PlayerSin * SummonDistance)
set BoatY to PlayerY + (PlayerCos * SummonDistance)
set BoatZ to PlayerZ
; Disable all the refs first since they need to be disabled and enabled one frame later to appear correctly
SetStage RYB 50 ; Disable boat and attachment refs
BoatRef.MoveTo Player
BoatRef.SetPos x, BoatX
BoatRef.SetPos y, BoatY
BoatRef.SetPos z, BoatZ
BoatRef.SetAngle z, BoatModelAngle
set Summoning to 2
return
endif
endif
if (SummonTimer > 0)
set SummonTimer to SummonTimer - SecondsPassed
elseif (SummonTimer <= 0 && Summoning == 1) ; time to move the boat to the collider
set BoatX to Collider.GetPos x
set BoatY to Collider.GetPos y
set BoatZ to Collider.GetPos z
set BoatZ to BoatZ - RockZOffset ; remove rocking offset from last frame to get "true" Z
if (BoatZ < (WaterLevelZ + 1))
set BoatZ to WaterLevelZ + 1
endif
SetStage RYB 50 ; Disable boat and attachment refs
BoatRef.MoveTo Player
BoatRef.SetPos x, BoatX
BoatRef.SetPos y, BoatY
BoatRef.SetPos z, BoatZ
BoatRef.SetAngle z, BoatModelAngle
Collider.Disable
set Summoning to 2
return
; set SummonTimer to SummonTimerDelay ; this delay maybe not necessary
elseif (Summoning == 2)
set Summoning to 0
SetStage RYB 51 ; Re-enable boat and attachment refs
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
set Resetting to 1
endif
if (LampOn == 1 && LampLit.GetDisabled == 1 && Resetting != -1)
set Resetting to 1
LampLit.Enable
LampUnlit.PlaySound3D SPLFireballFail
elseif (LampOn == 0 && LampLit.GetDisabled == 0 && Resetting != -1)
set Resetting to 1
LampLit.Disable
LampUnlit.PlaySound3D ITMTorchHeldExt
endif
set SecondsPassed to GetSecondsPassed
; Clamp extreme values
if (SecondsPassed < 0.001)
set SecondsPassed to 0.001
elseif (SecondsPassed > 0.1)
set SecondsPassed to 0.1
endif
; Exponentially smooth the delta time to adjust for frame rate changes
set SmoothedDeltaTime to ((1.0 - DeltaSmoothingFactor) * SmoothedDeltaTime) + (DeltaSmoothingFactor * SecondsPassed)
; Speed values assume a frame rate of 60fps so readjust speed values to current frame rate
set FrameRowForce to BaseRowForce * (SmoothedDeltaTime / TargetDeltaTime)
if (Rowing == 0 && AutoRowing == 0 && BoatMoving == 2)
; Boat is moving, but not rowing. Decay the velocity using exponential decay.
; velocity = velocity * (1 - decay * time)
if (BaseBoatVelocity != 0)
; Calculate decay factor based on smoothed delta time
; Convert to time-based decay
; retention^(frames) = retention^(time/frameTime)
; We need to calculate r^(SmoothedDeltaTime/TargetDeltaTime)
; Approximation since we can't do pow():
; For small time steps, r^t ≈ 1 + t*ln(r)
; ln(0.98) ≈ -0.0202
set FrameVelocityDecay to 1 + (SmoothedDeltaTime / TargetDeltaTime) * (VelocityDecayLnRetentionFactor)
set BaseBoatVelocity to BaseBoatVelocity * FrameVelocityDecay
; Stop when very slow
if (BaseBoatVelocity > -0.2 && BaseBoatVelocity < 0.2)
set BaseBoatVelocity to 0
set BoatMoving to 0
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
Seat.SetDestroyed 0
BoatMarker.Enable
Collider.Disable
endif
endif
endif
if ((Rowing == 1 || AutoRowing == 1) && BoatMoving == 2 && BaseBoatVelocity < BoatMaxVelocity)
; Don't allow the boat to accelerate forever past max speed
set BaseBoatVelocity to BaseBoatVelocity + FrameRowForce
if (BaseBoatVelocity >= BoatMaxVelocity)
set BaseBoatVelocity to BoatMaxVelocity
endif
endif
if ((Rowing == 2 || AutoRowing == 2) && BoatMoving == 2 && BaseBoatVelocity > -BoatMaxVelocity)
; Don't allow the boat to accelerate backwards forever past max negative speed
set BaseBoatVelocity to BaseBoatVelocity - FrameRowForce
if (BaseBoatVelocity <= -BoatMaxVelocity)
set BaseBoatVelocity to -BoatMaxVelocity
endif
endif
if (RowTimer > 0)
set RowTimer to RowTimer - SecondsPassed
elseif (RowTimer <= 0 && Rowing > 0)
set Rowing to 0
endif
if (BoatMoving == 2 && PlayerDistance > OverboardDistance)
set Rowing to 0
set CurrentTurnRate to 0 ; immediately stop turning
set AutoRowing to 0
set LadderDeployed to 1
Ladder.Enable
set Resetting to 1
Message "You've gone overboard!"
endif
if (BoatMoving == 2 && CollisionDetectTimer > 0)
set CollisionDetectTimer to CollisionDetectTimer - SecondsPassed
elseif (BoatMoving == 2 && CollisionDetectTimer <= 0 && Collider.GetInSameCell Player && Collider.GetPos z > CollisionDetectZThreshold)
set ColliderActualX to Collider.GetPos x
set ColliderActualY to Collider.GetPos y
if (ColliderActualX > ColliderX - ColliderPosThreshold && ColliderActualX < ColliderX + ColliderPosThreshold && ColliderActualY > ColliderY - ColliderPosThreshold && ColliderActualY < ColliderY + ColliderPosThreshold)
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
SetStage RYB 21 ; Collision procedure
endif
elseif (BoatMoving == 2 && CollisionDetectTimer <= 0 && Collider.GetDead == 1)
; Moving the collider every frame seems to cause the collider to spontaneously die when near land. However, this
; also seems to have many false positives where it dies in open water. Instead of every frame, the script uses
; ColliderMoveTimer to move it every N seconds instead of every frame. The Collider doesn't seem to
; spontaneously die with this method, but I kept this here just in-case it ever does happen.
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
Collider.Resurrect 0
SetStage RYB 21 ; Collision procedure
endif
if (BoatMoving >= 1 && (Player.GetSitting != 0 && Player.GetSitting != 3))
; wait for player to sit/standup before continuing to move
Message "Pausing the boat until you get up."
return
endif
; Boat dragging. See visualization here: https://claude.ai/public/artifacts/23380c6b-c9a4-430d-bd86-781ae588739f
if (Dragging >= 1)
set PlayerX to Player.GetPos x
set PlayerY to Player.GetPos y
set PlayerZ to Player.GetPos z
SetStage RYB 11 ; Update boat position and angle
if (PlayerDistance > DragMaxPlayerDistance)
set Dragging to 0
Player.RemoveItem RYBBoatToken 1
if (RockingEnabled == 0)
set fQuestDelayTime to MediumUpdateRate
endif
set DragBoatVelocityX to 0
set DragBoatVelocityY to 0
Seat.SetDestroyed 0
BoatMarker.Enable
Message "You got too far from the boat and stopped dragging it."
endif
; Drag point is at center of boat
set DragPointX to BoatX
set DragPointY to BoatY
; Vector from rope attach point (center of boat) to player
set dX to PlayerX - DragPointX
set dY to PlayerY - DragPointY
; Save absolute values of dX and dY
if (dX < 0)
set dXAbs to -dX
else
set dXAbs to dX
endif
if (dY < 0)
set dYAbs to -dY
else
set dYAbs to dY
endif
if (dXAbs < 0.1 && dYAbs < 0.1)
; Player is very close to the drag point, so no need to calculate angle
set DragTargetAngle to BoatAngle
else
SetStage RYB 10 ; Calculate DragTargetAngle with atan2(dY, dX)
endif
; Calculate squared distance from player to drag point
set DragTautDistanceSquared to ((PlayerX - DragPointX) * (PlayerX - DragPointX)) + ((PlayerY - DragPointY) * (PlayerY - DragPointY))
; Only apply force if rope is taut (player far enough away)
if (DragTautDistanceSquared > (DragRopeLength * DragRopeLength))
; Get actual distance using Newton's method for square root (2 iterations is usually enough)
; See: https://cs.uesp.net/wiki/Square_Root
set PlayerMovementDistance to DragRopeLength + 50 ; Initial reasonable guess
; Iteration 1: x = (x + n/x) / 2
set PlayerMovementDistance to (PlayerMovementDistance + (DragTautDistanceSquared / PlayerMovementDistance)) / 2
; Iteration 2: x = (x + n/x) / 2
set PlayerMovementDistance to (PlayerMovementDistance + (DragTautDistanceSquared / PlayerMovementDistance)) / 2
; Calculate normalized direction from drag point to player
if (PlayerMovementDistance > 0)
set DragForceX to (PlayerX - DragPointX) / PlayerMovementDistance
set DragForceY to (PlayerY - DragPointY) / PlayerMovementDistance
; Apply pulling force based on how far beyond rope length we are
set DragSlackDistance to PlayerMovementDistance - DragRopeLength
; Instead of linear, use a dampened response
if (DragSlackDistance > 100)
; If very far, use logarithmic-like response to prevent extreme forces
set DragSlackDistance to 100 + (DragSlackDistance - 100) * 0.1
endif
set PlayerMovementForce to DragSlackDistance * DragPullStrength
if (PlayerMovementForce > DragMaxForce)
set PlayerMovementForce to DragMaxForce
endif
; Simple spring force: the further stretched, the stronger the pull
set DragForceX to DragForceX * PlayerMovementForce
set DragForceY to DragForceY * PlayerMovementForce
; Apply force to velocity
set DragBoatVelocityX to DragBoatVelocityX + (DragForceX * SmoothedDeltaTime / TargetDeltaTime)
set DragBoatVelocityY to DragBoatVelocityY + (DragForceY * SmoothedDeltaTime / TargetDeltaTime)
; Limit max velocity
set DragVelocitySquared to (DragBoatVelocityX * DragBoatVelocityX) + (DragBoatVelocityY * DragBoatVelocityY)
if (DragVelocitySquared > (DragMaxVelocity * DragMaxVelocity))
; Get actual velocity
set PlayerMovementDistance to DragMaxVelocity ; Start with max
set PlayerMovementDistance to (PlayerMovementDistance + (DragVelocitySquared / PlayerMovementDistance)) / 2
; Scale down to max velocity
set DragBoatVelocityX to DragBoatVelocityX * (DragMaxVelocity / PlayerMovementDistance)
set DragBoatVelocityY to DragBoatVelocityY * (DragMaxVelocity / PlayerMovementDistance)
endif
; Calculate angle difference to movement direction
set DragAngleDiff to DragTargetAngle - BoatAngle
if (DragAngleDiff > 180)
set DragAngleDiff to DragAngleDiff - 360
elseif (DragAngleDiff < -180)
set DragAngleDiff to DragAngleDiff + 360
endif
if (DragAngleDiff < DragTurnDeadzone && DragAngleDiff > -DragTurnDeadzone)
; Within deadzone - decay the turn rate
set DragCurrentTurnRate to DragCurrentTurnRate * DragTurnRateDeceleration
; Stop tiny movements
if (DragCurrentTurnRate > -0.01 && DragCurrentTurnRate < 0.01)
set DragCurrentTurnRate to 0
endif
else
; Outside deadzone - calculate target turn rate and ramp up toward it
set DragFrameTurnRate to DragAngleDiff * DragBaseTurnRate * (SmoothedDeltaTime / TargetDeltaTime)
if (DragFrameTurnRate > DragMaxTurnPerFrame)
set DragFrameTurnRate to DragMaxTurnPerFrame
elseif (DragFrameTurnRate < -DragMaxTurnPerFrame)
set DragFrameTurnRate to -DragMaxTurnPerFrame
endif
; Smoothly ramp up to target turn rate
set DragCurrentTurnRate to DragCurrentTurnRate + ((DragFrameTurnRate - DragCurrentTurnRate) * DragTurnRateAcceleration * SmoothedDeltaTime / TargetDeltaTime)
endif
set BoatAngle to BoatAngle + DragCurrentTurnRate
endif
endif
; Apply friction to velocity (always, even when not pulling)
set DragBoatVelocityX to DragBoatVelocityX * DragFriction
set DragBoatVelocityY to DragBoatVelocityY * DragFriction
; Update boat position based on velocity
set BoatX to BoatX + (DragBoatVelocityX * SmoothedDeltaTime / TargetDeltaTime)
set BoatY to BoatY + (DragBoatVelocityY * SmoothedDeltaTime / TargetDeltaTime)
; Only allow changing Z pos and pitch angle if rope is taut so the player can't ride the boat up into the air
; (not a plane mod yet ;D)
if (DragTautDistanceSquared > (DragRopeLength * DragRopeLength))
; Smoothly interpolate boat Z towards target
if (Player.IsSwimming == 1)
set DragPlayerBoatZDiff to PlayerZ - BoatZ
else
; Raise the boat up a bit when on land so it doesn't clip into the ground as much
set DragPlayerBoatZDiff to (PlayerZ + DragLandPlayerZOffset) - BoatZ
endif
; Calculate distance boat has traveled from start of path segment
set dX to PlayerX - DragPathStartX
set dY to PlayerY - DragPathStartY
set DragPathDistance to (dX * dX) + (dY * dY)
; If boat has moved enough, calculate the slope it traveled
if (DragPathDistance > (DragPathMinDistance * DragPathMinDistance))
; Get actual distance using Newton's method
set DragPathDistance to DragPathMinDistance ; Initial guess
set DragPathDistance to (DragPathDistance + ((dX * dX + dY * dY) / DragPathDistance)) / 2
set DragPathDistance to (DragPathDistance + ((dX * dX + dY * dY) / DragPathDistance)) / 2
; Calculate slope from player's actual path
set DragPathSlope to ((PlayerZ - DragPathStartZ) / DragPathDistance) * radToDeg * -DragPathSlopeFactor
; Apply deadzone to ignore tiny slopes
if (DragPathSlope > -DragPathSlopeDeadzone && DragPathSlope < DragPathSlopeDeadzone)
set DragPathSlope to 0
endif
; Clamp calculated slope to reasonable values
if (DragPathSlope > DragMaxPitchAngle)
set DragPathSlope to DragMaxPitchAngle
elseif (DragPathSlope < -DragMaxPitchAngle)
set DragPathSlope to -DragMaxPitchAngle
endif
; Start new path segment from current position
set DragPathStartX to PlayerX
set DragPathStartY to PlayerY
set DragPathStartZ to PlayerZ
endif
if (Player.IsSwimming == 1)
set DragTargetPitchAngle to 0 ; Reset pitch when swimming
else
; Only adjust pitch when boat is moving and player is not swimming
if (DragBoatVelocityX != 0 || DragBoatVelocityY != 0)
; Smoothly adjust target pitch toward the path slope
set DragTargetPitchAngle to DragTargetPitchAngle + ((DragPathSlope - DragTargetPitchAngle) * DragTargetPitchMovingSmoothingFactor)
else
; When stopped, very slowly tend toward path slope
set DragTargetPitchAngle to DragTargetPitchAngle + ((DragPathSlope - DragTargetPitchAngle) * DragTargetPitchStoppedSmoothingFactor)
endif
; Z adjustment based on pitch angle to prevent clipping going up and down hills
if (DragPlayerBoatZDiff >= 0)
; raise boat when player going downhill
set DragPlayerBoatZDiff to DragPlayerBoatZDiff + (DragTargetPitchAngle * DragDownhillZAdjustmentFactor)
else
; lower boat when player going uphill (DragTargetPitchAngle is negative)
set DragPlayerBoatZDiff to DragPlayerBoatZDiff + (DragTargetPitchAngle * DragUphillZAdjustmentFactor)
endif
endif
endif
if (Dragging == 2 || DragTautDistanceSquared > (DragRopeLength * DragRopeLength))
set DragFrameBoatZAdjust to (DragPlayerBoatZDiff * DragZInterpolationRate * SmoothedDeltaTime / TargetDeltaTime)
if (Dragging == 2)
; This is a one-time adjustment, so remove this frame's adjustment from the total target adjustment
set DragPlayerBoatZDiff to DragPlayerBoatZDiff - DragFrameBoatZAdjust
if ((DragFrameBoatZAdjust > 0 && DragPlayerBoatZDiff <= 0) || (DragFrameBoatZAdjust < 0 && DragPlayerBoatZDiff > 0) || DragPlayerBoatZDiff == 0)
set Dragging to 1 ; target reached, proceed with normal dragging
endif
endif
; Prevent "boat becomes a missile" issue
if (DragFrameBoatZAdjust > DragMaxZPerFrame)
set DragFrameBoatZAdjust to DragMaxZPerFrame
elseif (DragFrameBoatZAdjust < -DragMaxZPerFrame)
set DragFrameBoatZAdjust to -DragMaxZPerFrame
endif
set BoatZ to BoatZ + DragFrameBoatZAdjust
endif
; Prevent boat from going below water level
if (BoatZ < (WaterLevelZ + 1))
set BoatZ to WaterLevelZ + 1
endif
; Stop tiny movements
if (DragBoatVelocityX > -0.1 && DragBoatVelocityX < 0.1)
set DragBoatVelocityX to 0
endif
if (DragBoatVelocityY > -0.1 && DragBoatVelocityY < 0.1)
set DragBoatVelocityY to 0
endif
endif
; Smooth the actual pitch angle toward target (outside the dragging check)
if (DragPitchAngle != DragTargetPitchAngle)
set DragPitchAngle to DragPitchAngle + ((DragTargetPitchAngle - DragPitchAngle) * DragPitchSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
; Stop tiny movements
if (DragPitchAngle > -0.05 && DragPitchAngle < 0.05)
set DragPitchAngle to 0
endif
endif
; TODO: test if it's necessary to set these every update while moving.
; In theory, they only need to be set once at initialization since the boat is immovable beyond this script.
; (I want to see if resetting these fixes get on boat issues)
; Doesn't 100% fix it, but maybe it helps a bit? Not sure.
if (Summoning == 0 && Dragging == 0)
; During summoning and dragging these are preset so don't overwrite them
SetStage RYB 11 ; Update boat position and angle
endif
; Calculate player weight influence on boat pitch and roll
if (PlayerWeightEnabled == 1 && Summoning == 0 && Dragging == 0 && PlayerDistance < PlayerWeightMaxDistanceForward)
; Get player position
set PlayerX to Player.GetPos x
set PlayerY to Player.GetPos y
set PlayerZ to Player.GetPos z
; Calculate player position relative to boat center
set PlayerRelativeX to PlayerX - BoatX
set PlayerRelativeY to PlayerY - BoatY
; Calculate boat's forward and right vectors using BoatAngle directly
; Forward vector (bow direction): sin(BoatAngle), cos(BoatAngle)
; Right vector (starboard direction): cos(BoatAngle), -sin(BoatAngle)
; PlayerLocalY: positive = toward bow, negative = toward stern
set PlayerLocalY to PlayerRelativeX * sin + PlayerRelativeY * cos
; PlayerLocalX: positive = toward starboard, negative = toward port
set PlayerLocalX to PlayerRelativeX * cos - PlayerRelativeY * sin
set PlayerLocalZ to PlayerZ - BoatZWithRock
; Calculate distance from boat center for falloff effect
set PlayerDistanceFromCenter to PlayerRelativeX * PlayerRelativeX + PlayerRelativeY * PlayerRelativeY
; Newton's method square root approximation (2 iterations)
set PlayerDistanceFromCenter to PlayerWeightMaxDistanceForward ; Initial guess
set PlayerDistanceFromCenter to (PlayerDistanceFromCenter + ((PlayerRelativeX * PlayerRelativeX + PlayerRelativeY * PlayerRelativeY) / PlayerDistanceFromCenter)) / 2
set PlayerDistanceFromCenter to (PlayerDistanceFromCenter + ((PlayerRelativeX * PlayerRelativeX + PlayerRelativeY * PlayerRelativeY) / PlayerDistanceFromCenter)) / 2
; Calculate influence factor (1.0 at center, 0.0 at max distance)
; Limit effect area to a box approximately the area above the boat up 60 units (PlayerWeightMaxDistanceVertical)
if (PlayerDistanceFromCenter <= PlayerWeightMaxDistanceForward && PlayerLocalZ < PlayerWeightMaxDistanceVertical && PlayerLocalX < PlayerWeightMaxDistanceSide && PlayerLocalX > -PlayerWeightMaxDistanceSide)
set PlayerWeightInfluence to 1.0 - (PlayerDistanceFromCenter / PlayerWeightMaxDistanceForward)
else
set PlayerWeightInfluence to 0
endif
; Calculate target pitch offset based on player's fore/aft position
; Positive PlayerLocalY = toward bow (front) = boat should pitch forward (bow dips down)
; Negative PlayerLocalY = toward stern (back) = boat should pitch backward (stern dips down)
set TargetPlayerWeightPitchOffset to PlayerLocalY * PlayerWeightPitchFactor * PlayerWeightInfluence
; Calculate target roll offset based on player's port/starboard position
; Positive PlayerLocalX = toward starboard (right) = boat should roll starboard (startboard dips down)
; Negative PlayerLocalX = toward port (left) = boat should roll port (port dips down)
set TargetPlayerWeightRollOffset to -PlayerLocalX * PlayerWeightRollFactor * PlayerWeightInfluence
; Smooth the transition to prevent jarring movements
set PlayerWeightPitchOffset to PlayerWeightPitchOffset + ((TargetPlayerWeightPitchOffset - PlayerWeightPitchOffset) * PlayerWeightSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set PlayerWeightRollOffset to PlayerWeightRollOffset + ((TargetPlayerWeightRollOffset - PlayerWeightRollOffset) * PlayerWeightSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
else
; Player not on boat or system disabled - gradually return to neutral
set PlayerWeightPitchOffset to PlayerWeightPitchOffset * (1 - PlayerWeightSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set PlayerWeightRollOffset to PlayerWeightRollOffset * (1 - PlayerWeightSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
; Stop tiny movements
if (PlayerWeightPitchOffset > -0.01 && PlayerWeightPitchOffset < 0.01)
set PlayerWeightPitchOffset to 0
endif
if (PlayerWeightRollOffset > -0.01 && PlayerWeightRollOffset < 0.01)
set PlayerWeightRollOffset to 0
endif
endif
if (BoatMoving >= 1 || Dragging >= 1 || Resetting >= 1)
; Speed values assume a frame rate of 60fps so readjust speed values to current frame rate
set FrameBoatVelocity to BaseBoatVelocity * (SmoothedDeltaTime / TargetDeltaTime)
; Calculate new boat angle. Script originally by Jason1
set PlayerAngle to Player.GetAngle z
if (PlayerAngle < 0)
set PlayerAngle to PlayerAngle + 360
elseif (PlayerAngle >= 360)
set PlayerAngle to PlayerAngle - 360
endif
if (BoatMoving >= 1 && Dragging == 0 && Summoning == 0 && LockHeading == 0)
set Diff to PlayerAngle - BoatAngle
; Calculate velocity-based turn modifier to make boats turn slower when moving slowly for realism
if (BaseBoatVelocity < 0)
set AbsoluteBoatVelocity to -BaseBoatVelocity
else
set AbsoluteBoatVelocity to BaseBoatVelocity
endif
; Turn rate scales with velocity (0.3 to 1.0 multiplier)
set TurnRateVelocityFactor to 0.3 + (0.7 * (AbsoluteBoatVelocity / BoatMaxVelocity))
if (TurnRateVelocityFactor > 1.0)
set TurnRateVelocityFactor to 1.0
endif
set FrameTurnRate to BaseTurnRate * TurnRateVelocityFactor * (SmoothedDeltaTime / TargetDeltaTime)
if ((Diff > 1 || Diff < -1) && (Diff > TurnDeadzone || Diff < -TurnDeadzone))
; Player is currently turning, ramp up turn rate
set CurrentTurnRate to CurrentTurnRate + ((FrameTurnRate - CurrentTurnRate) * TurnRateAcceleration * SmoothedDeltaTime / TargetDeltaTime)
else
; Player not turning, decay the turn rate
set CurrentTurnRate to CurrentTurnRate * TurnRateDeceleration
; Stop tiny movements
if (CurrentTurnRate > -0.05 && CurrentTurnRate < 0.05)
set CurrentTurnRate to 0
endif
endif
if (CurrentTurnRate != 0 && (Diff > 1 || Diff < -1))
if (PlayerAngle > 180)
set Hemi to PlayerAngle - 180
else
set Hemi to PlayerAngle + 180
endif
if (Hemi < 0)
set Hemi to Hemi + 360
elseif (Hemi >= 360)
set Hemi to Hemi - 360
endif
if (PlayerAngle > Hemi)
if (BoatAngle > Hemi && BoatAngle < PlayerAngle)
set BoatAngle to BoatAngle + CurrentTurnRate
else
set BoatAngle to BoatAngle - CurrentTurnRate
endif
else
if (BoatAngle < Hemi && BoatAngle > PlayerAngle)
set BoatAngle to BoatAngle - CurrentTurnRate
else
set BoatAngle to BoatAngle + CurrentTurnRate
endif
endif
endif
if (BoatAngle < 0)
set BoatAngle to BoatAngle + 360
elseif (BoatAngle >= 360)
set BoatAngle to BoatAngle - 360
endif
else
; Player not turning - decay the turn rate
set CurrentTurnRate to CurrentTurnRate * TurnRateDeceleration
; Stop tiny movements
if (CurrentTurnRate > -0.05 && CurrentTurnRate < 0.05)
set CurrentTurnRate to 0
endif
endif
; Adjust for model alignment (boat model is rotated 90 degrees)
set BoatModelAngle to BoatAngle + 90
if (BoatModelAngle < 0)
set BoatModelAngle to BoatModelAngle + 360
elseif (BoatModelAngle >= 360)
set BoatModelAngle to BoatModelAngle - 360
endif
set BoatX to BoatX + (sin * FrameBoatVelocity)
set BoatY to BoatY + (cos * FrameBoatVelocity)
if (BoatMoving >= 1 && Dragging == 0 && OnLand == 0 && BoatZ != (WaterLevelZ + 1))
set BoatZ to WaterLevelZ + 1 ; if boat started moving above water, reset it to water level
endif
set BoatZWithRock to BoatZ + RockZOffset
if (Resetting != 2)
BoatRef.MoveTo Player
endif
BoatRef.SetPos x, BoatX
BoatRef.SetPos y, BoatY
BoatRef.SetPos z, BoatZWithRock
BoatRef.SetAngle z, BoatModelAngle
BoatRef.SetAngle x, BoatPitchAngle
BoatRef.SetAngle y, BoatRollAngle
endif
if (BoatZ > LandZThreshold)
set OnLand to 1
else
set OnLand to 0
endif
; Boat rocking animation
if (RockingEnabled == 1 && Dragging == 0 && Summoning == 0 && Grounded == 0 && OnLand == 0 && PlayerNearBoat == 1)
; Update random variation
if (RockRandomTimer <= 0)
set RockRandomTimer to RockRandomInterval
; Add some random variation to make it look natural
set RockRandomPhase to (GetRandomPercent - 50) * 0.02
else
set RockRandomTimer to RockRandomTimer - SecondsPassed
endif
; Update rocking phases
set RockPhase to RockPhase + (RockFrequency * SecondsPassed)
if (RockPhase >= 360)
set RockPhase to RockPhase - 360
endif
set RockPhase2 to RockPhase2 + (RockFrequency2 * SecondsPassed)
if (RockPhase2 >= 360)
set RockPhase2 to RockPhase2 - 360
endif
set RockPhase3 to RockPhase3 + (RockFrequency3 * SecondsPassed)
if (RockPhase3 >= 360)
set RockPhase3 to RockPhase3 - 360
endif
SetStage RYB 15 ; Calculate sine for primary rocking (Z movement and pitch)
SetStage RYB 16 ; Calculate sine for secondary rocking
SetStage RYB 17 ; Calculate sine and cosine for roll motion
; Calculate rocking offsets
; Z movement: combine multiple sine waves
set TargetRockZOffset to RockAmplitudeZ * (rockSin * 0.7 + rockSin2 * 0.3 + RockRandomPhase)
; Pitch (X rotation): boat tips forward/backward
set TargetRockPitchOffset to RockAmplitudePitch * (rockSin * 0.8 + rockSin2 * 0.2) + PlayerWeightPitchOffset
; Roll (Y rotation): boat tips side to side, offset by 90 degrees using cosine
set TargetRockRollOffset to RockAmplitudeRoll * (rockCos3 * 0.7 + rockSin2 * 0.3) + PlayerWeightRollOffset
; Adjust for boat speed (more rocking when moving)
if (BoatMoving >= 1)
if (BaseBoatVelocity < 0)
set AbsoluteBoatVelocity to -BaseBoatVelocity
else
set AbsoluteBoatVelocity to BaseBoatVelocity
endif
; Increase rocking amplitude based on speed
set TargetRockZOffset to TargetRockZOffset * (1 + (AbsoluteBoatVelocity / BoatMaxVelocity) * RockSpeedFactor)
set TargetRockPitchOffset to TargetRockPitchOffset * (1 + (AbsoluteBoatVelocity / BoatMaxVelocity) * RockSpeedFactor * 1.5)
set TargetRockRollOffset to TargetRockRollOffset * (1 + (AbsoluteBoatVelocity / BoatMaxVelocity) * RockSpeedFactor * 0.7)
endif
; Apply weather factor (based on wind speed)
set WindSpeed to GetWindSpeed
set TargetRockZOffset to TargetRockZOffset * (1 + (WindSpeed * RockWeatherFactor))
; Limit extreme Z offsets that would mess with boat-in-water detection
if (BoatZ + TargetRockZOffset < -RockMaxAbsoluteZ)
set TargetRockZOffset to -RockMaxAbsoluteZ - BoatZ
elseif (BoatZ + TargetRockZOffset > RockMaxAbsoluteZ)
set TargetRockZOffset to RockMaxAbsoluteZ - BoatZ
endif
set TargetRockPitchOffset to TargetRockPitchOffset * (1 + (WindSpeed * RockWeatherFactor))
set TargetRockRollOffset to TargetRockRollOffset * (1 + (WindSpeed * RockWeatherFactor))
; Smooth out the rocking effects so it doesn't snap into rocking position on boat launch
set RockZOffset to RockZOffset + ((TargetRockZOffset - RockZOffset) * RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set RockPitchOffset to RockPitchOffset + ((TargetRockPitchOffset - RockPitchOffset) * RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set RockRollOffset to RockRollOffset + ((TargetRockRollOffset - RockRollOffset) * RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
else
; Dampen rocking when conditions aren't met
set RockZOffset to RockZOffset * (1 - RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set RockPitchOffset to RockPitchOffset * (1 - RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
set RockRollOffset to RockRollOffset * (1 - RockSmoothingFactor * SmoothedDeltaTime / TargetDeltaTime)
; Stop tiny movements
if (RockZOffset > -0.01 && RockZOffset < 0.01)
set RockZOffset to 0
endif
if (RockPitchOffset > -0.01 && RockPitchOffset < 0.01)
set RockPitchOffset to 0
endif
if (RockRollOffset > -0.01 && RockRollOffset < 0.01)
set RockRollOffset to 0
endif
endif
if (BoatMoving >= 1 || Dragging >= 1 || Resetting >= 1 || (PlayerNearBoat == 1 && (RockZOffset != 0 || RockPitchOffset != 0 || RockRollOffset != 0)))
set ang to BoatAngle
SetStage RYB 12 ; Calculate sin, cos, & tan for boat angle
endif
if (BoatMoving >= 1 || Dragging >= 1 || Resetting >= 1 || (PlayerNearBoat == 1 && (RockZOffset != 0 || RockPitchOffset != 0 || RockRollOffset != 0)))
; Transform world pitch/roll to boat's local pitch/roll
set BoatPitchAngle to (RockPitchOffset + DragPitchAngle) * cos + RockRollOffset * sin
set BoatRollAngle to -(RockPitchOffset + DragPitchAngle) * sin + RockRollOffset * cos
; Calculate sine/cosine for the transformed angles
if (BoatPitchAngle != 0)
SetStage RYB 13 ; Calculate sin, cos for pitch
else
set SinPitch to 0
set CosPitch to 1
endif
if (BoatRollAngle != 0)
SetStage RYB 14 ; Calculate sin, cos for roll
else
set SinRoll to 0
set CosRoll to 1
endif
endif
if (BoatMoving >= 1 || Dragging >= 1 || Resetting >= 1)
set BoatMarkerZ to BoatZWithRock + BoatMarkerZOffset
if (Resetting == 2)
BoatMarker.MoveTo BoatRef
else
BoatMarker.MoveTo Player
endif
BoatMarker.SetPos x, BoatX
BoatMarker.SetPos y, BoatY
BoatMarker.SetPos z, BoatMarkerZ
if (BoatMoving >= 1)
if (FrameBoatVelocity > 0)
set ColliderX to BoatX + (sin * ColliderOffset)
set ColliderY to BoatY + (cos * ColliderOffset)
else
set ColliderX to BoatX - (sin * ColliderOffsetReverse)
set ColliderY to BoatY - (cos * ColliderOffsetReverse)
endif
if (ColliderMoveTimer > 0)
Set ColliderMoveTimer to ColliderMoveTimer - SecondsPassed
else
SetStage RYB 20 ; Enable and position collider
Set ColliderMoveTimer to ColliderMoveFreq
endif
endif
elseif (BoatMoving == 0 && Dragging == 0 && Summoning == 0 && (PlayerNearBoat == 1 && (RockZOffset != 0 || RockPitchOffset != 0 || RockRollOffset != 0)))
; Apply rocking to stationary boat.
set BoatZWithRock to BoatZ + RockZOffset
BoatRef.SetPos z, BoatZWithRock
BoatRef.SetAngle x, BoatPitchAngle
BoatRef.SetAngle y, BoatRollAngle
set BoatMarkerZ to BoatZWithRock + BoatMarkerZOffset
BoatMarker.SetPos z, BoatMarkerZ
endif
; Update attachments (seat, chest, lamp, ladder) positions and angles both while moving and stationary (if rocking)
if (BoatMoving >= 1 || Dragging >= 1 || Resetting >= 1 || (BoatMoving == 0 && Dragging == 0 && (PlayerNearBoat == 1 && (RockZOffset != 0 || RockPitchOffset != 0 || RockRollOffset != 0))))
SetStage RYB 30 ; Calculate seat position and angle
SetStage RYB 40 ; Update seat position and angle
if (ChestPurchased == 1)
SetStage RYB 31 ; Calculate chest position and angle
SetStage RYB 41 ; Update chest position and angle
endif
if (LampPurchased == 1)
SetStage RYB 32 ; Calculate lamp(s) position and angle
SetStage RYB 42 ; Update lamp(s) position and angle
endif
if (LadderPurchased == 1 && LadderDeployed == 1)
SetStage RYB 33 ; Calculate ladder position and angle
SetStage RYB 43 ; Update ladder position and angle
endif
endif
if (BoatMoving == 1)
set BoatMoving to 2 ; boat is now fully in motion
endif
if (Resetting >= 1)
set Resetting to 0
endif
end