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: 40) 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) short DragEncumbranceEnabled ; 0 = disabled, 1 = enabled (default: 1) ; 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 set LandZThreshold to 40.0 set DragEncumbranceEnabled to 1 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 if (DragEncumbranceEnabled == 1) Player.AddItem RYBBoatToken 1 endif 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