commit f5fd13eb8afddf1e42e598a08d7d28ab38d381b2 Author: Tyler Hallada Date: Wed Jun 4 22:11:31 2025 -0400 Initial commit version 0.1.0 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c3bcbd8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 + +- Initial Release \ No newline at end of file diff --git a/Docs/Manual.xml b/Docs/Manual.xml new file mode 100644 index 0000000..69fe9b0 --- /dev/null +++ b/Docs/Manual.xml @@ -0,0 +1,189 @@ +

+
+
+THE MARINER'S GUIDE TO ENCHANTED VESSELS
+
+A Complete Guide to Rowboat Operation
+
+
+Being a comprehensive manual for the proper use and maintenance of enchanted watercraft, as approved by the Imperial Maritime Guild
+
+
Forward
+
+
Congratulations on your acquisition of a genuine Cyrodiilian rowboat! Whether you seek the tranquil waters of Lake Rumare for an afternoon's fishing, or dare to brave the treacherous channels of the Niben Bay, this sturdy vessel shall serve as your faithful companion upon Tamriel's waterways.
+
+This manual, penned by the master shipwrights of the Imperial City, contains all knowledge necessary for the proper operation of your enchanted craft. Study it well, for the waters of Cyrodiil can be as unforgiving as they are beautiful.
+
+
Chapter I: Basic Operation
+
+
Your rowboat has been imbued with minor enchantments that respond to both physical and magical commands. There exist two primary methods of propulsion:
+
+The Art of Rowing
+
+The most traditional method employs the "Row" spell, which you discover upon touching your vessel for the first time. When cast, this spell channels your stamina into forward momentum, as if you were pulling mighty oars through the water. Should you wish to reverse direction, simply adopt a crouched stance while casting - the enchantment will understand your intent and propel the craft backward. Those of hearty constitution may maintain the spell's casting for extended periods, though take care not to exhaust yourself completely whilst far from shore!
+
+Enchanted Propulsion
+
+For those who prefer to conserve their strength, your boat responds to verbal commands. Simply touch the vessel's hull and speak your intent - whether to "row forward" or "row backward" - and the boat's enchantments will maintain steady progress without further effort on your part.
+
+
Chapter II: Navigation and Steering
+
+
The boat's enchantments are attuned to your gaze and intent. When standing upon the deck, simply look in your desired direction of travel, and the vessel will gradually turn to match your heading.
+
+Note from the Enchanter's Guild: Those who favor the perspective of eagles - viewing themselves from above - must physically orient their body in the desired direction, as the enchantment cannot divine intent from such a vantage.
+
+Maintaining Course
+
+Should you wish to observe the scenery whilst maintaining your present heading, touch the hull and speak the command to "lock heading." The boat will then maintain its course regardless of where you turn your gaze. To restore normal steering, simply command it to "unlock heading."
+
+
Chapter III: Overland Transport
+
+
Through clever application of levitation enchantments, your boat may be transported across dry land - though not without considerable effort! To engage this feature, touch the hull and express your desire to "drag the boat." You may also trigger this enchantment through casting the row spell while near the boat.
+
+Be warned: the boat's full weight of two hundred pounds will rest upon your shoulders during transport. Many a sailor has found themselves caught by bandits or wild beasts whilst so encumbered! You may release the boat at any time by dropping it from your pack or touching the hull once more.
+
+
Chapter IV: Summoning Your Vessel
+
+
Through the application of advanced translocation magics, your boat may be called to your location from great distances. This enchantment proves invaluable when your vessel becomes lodged amongst rocks or when you simply cannot recall where you moored it last evening.
+
+Standard Summoning
+
+The primary summoning ritual calls your boat to the most suitable location before you. The enchantment's intelligence will seek solid ground or calm water, avoiding obstacles that might damage the hull. Simply speak the summoning command when not upon the boat, and it shall appear at a safe distance ahead.
+
+Direct Placement
+
+For those situations requiring precise positioning, a secondary command exists to place the boat directly before you. Be warned - this method bypasses the enchantment's safety measures! Your boat may manifest suspended in mid-air or partially merged with solid objects. Use this method only when the standard summoning proves inadequate.
+
+A word of caution: The translocation process requires several moments to complete. Do not attempt to board your vessel until it has fully materialized and settled into position.
+
+
Chapter V: Maritime Enhancements
+
+
Master Sergius Verus of Three Brothers Trade Goods in the Imperial City's Market District offers several improvements for the discerning mariner:
+
+Rowboat Storage Chest
+
+A waterproof strongbox enchanted with preservation magics. Any items stored within shall remain safe from both thieves and the ravages of time. Unlike common containers, this chest will never mysteriously empty itself of your possessions.
+
+Rowboat Lamp
+
+A specially crafted lamp that provides illumination during night voyages. It may be lit or extinguished by touch, through the boat's command system, or by application of fire and frost magics respectively. Let flame kindle the wick, or frost snuff it out!
+
+Rowboat Rope Ladder
+
+A rope ladder imbued with protective enchantments. Should you fall overboard whilst the boat moves through open water, this ladder will automatically deploy, allowing you to regain the deck. A wise investment for those prone to maritime mishaps!
+
+
Chapter VI: Safety and Maintenance
+
+
General Precautions
+
+Always ensure you are within reasonable distance of your vessel when attempting to row. The enchantments cannot reach beyond approximately three hundred feet.
+
+Your boat will cease all movement should you find yourself overboard, save for when the safety ladder deploys.
+
+Take care when navigating shallow waters or near shorelines - your boat may become grounded upon rocks or sand.
+
+The boat's enchantments prevent operation whilst seated in meditation or rest. Stand ready at the helm!
+
+Collision Detection Peculiarities
+
+The protective enchantments that prevent your boat from merging with solid matter can occasionally prove... temperamental. You may find your vessel suddenly "grounded" whilst in open water - fear not! This is merely the enchantment being overly cautious. Simply continue rowing and the boat will free itself.
+
+Conversely, these same protections may sometimes slumber when most needed, allowing your boat to become lodged within cliff faces or other obstacles. Should this occur, employ the summoning ritual to recall your vessel to a clear location.
+
+Navigation and Location
+
+Fear not losing track of your vessel! The boat bears a minor cartographic enchantment that marks its position upon your map whenever it comes to rest. Look for its marker when you need to retrieve your craft.
+
+Boarding Your Vessel
+
+The standard boarding command attempts to place you atop the deck, though the enchantment sometimes miscalculates, depositing you in the water instead.
+
+A more reliable method involves using the boat's seat - this forcibly positions you aboard, though in a seated position.
+
+Those who have invested in the safety ladder may activate it whilst the boat is stationary for the same reliable boarding as the seat.
+
+Choose your method based on circumstances and how wet you're willing to become!
+
+Interior Waterways
+
+Be especially cautious when navigating caves, grottos, and other enclosed spaces with water. The boat's water-level detection enchantments can become confused by the conflicting magical auras common to such places. Your vessel may behave erratically, believing itself to be on dry land whilst clearly afloat, or vice versa.
+
+A Final Word of Wisdom
+
+Should you encounter any behavior not described in this manual, remember that your boat is a complex marriage of carpentry and enchantment. Like any magical artifact, it may occasionally surprise even its creators. When in doubt, disembark, summon the vessel anew, and approach with patience and good humor.
+
+
Appendix: Advanced Enchantment Modifications
+
+
The following section is intended only for those versed in the highest arts of maritime enchantment. Improper modification may result in unstable or dangerous vessel behavior.
+
+For those brave souls who would tune their vessel's enchantments, speak the ancient console invocation (`) and recite:
+
+ObvConsole set RYB.ENCHANTMENT to VALUE
+
+Movement Enchantments:
+BoatMaxVelocity (6) - Maximum speed attainable
+BaseRowForce (0.05) - Force applied when rowing
+BaseTurnRate (0.4) - Base turning speed
+TurnDeadzone (10) - Degrees before turning begins
+VelocityDecayLnRetentionFactor (-0.01005) - Speed depletion rate
+TurnRateAcceleration (0.15) - Turn start speed
+TurnRateDeceleration (0.92) - Turn stop speed
+
+Dragging Enchantments:
+DragRopeLength (250) - Distance before boat follows
+DragMaxVelocity (4.0) - Maximum dragging speed
+DragFriction (0.85) - Resistance when dragging
+DragZInterpolationRate (0.1) - Speed of height adjustment
+DragBaseTurnRate (0.1) - How quickly boat turns
+DragMaxTurnPerFrame (2.0) - Max speed of boat turns
+DragTurnDeadzone (5) - Degrees of diversion before boat turns
+DragLandPlayerZOffset (30) - Height of boat off ground
+DragTurnRateAcceleration (0.15) - Turn start speed
+DragTurnRateDeceleration (0.92) - Turn stop speed
+DragPathSlopeFactor (0.7) - Tilt angle sensitivity
+DragPathSlopeDeadzone (2) - Minimum slope of land to tilt
+DragMaxPitchAngle (45) - Maximum tilt angle on slopes
+DragPitchSmoothingFactor (0.15) - Smoothness of tilting
+DragPathMinDistance (50) - Distance to drag before adjusting tilt
+DragUphillZAdjustmentFactor (6) - Amount to lower boat dragging uphill
+DragDownhillZAdjustmentFactor (6) - Amount to raise boat dragging downhill
+
+Wave Simulation Enchantments:
+RockingEnabled (1) - Enable water motion effects
+RockAmplitudeZ (0.8) - Vertical rocking magnitude
+RockAmplitudePitch (2.0) - Forward/back tilt magnitude
+RockAmplitudeRoll (3.0) - Side-to-side tilt magnitude
+RockFrequency (30.0) - Primary rocking speed
+RockFrequency2 (30.0) - Secondary rocking speed
+RockFrequency3 (30.0) - Tertiary rocking (roll) speed
+RockWeatherFactor (0.5) - Weather influence multiplier
+RockSpeedFactor (0.25) - Boat speed influence multiplier
+RockDistanceThreshold (3000) - Distance from boat rocking ceases
+RockSmoothingFactor (0.1) - Smoothness of rocking
+
+Weight Distribution Enchantments:
+PlayerWeightEnabled (1) - Enable weight effects
+PlayerWeightPitchFactor (0.15) - Forward/back tilt from position
+PlayerWeightRollFactor (0.12) - Side tilt from position
+PlayerWeightSmoothingFactor (0.2) - How fast tilt changes
+PlayerWeightMaxDistanceForward (240) - Distance lengthwise effect applies
+PlayerWeightMaxDistanceSide (100) - Distance sideways effect applies
+PlayerWeightMaxDistanceVertical (60) - Distance upwards effect applies
+
+Collision Detection Enchantments:
+CollisionDetectDelay (2) - Delay before checking obstacles
+CollisionDetectZThreshold (10) - Height threshold for collision
+ColliderOffset (300) - Distance forward from center for detection
+ColliderOffsetReverse (350) - Distance backward from center for detection
+ColliderMoveFreq (0.05) - How often to move collider
+OverboardDistance (300) - Distance before "overboard" triggers
+
+Miscellaneous Enchantments:
+SummonDistance (350) - Distance boat appears when summoned
+LandZThreshold (10) - Height above water considered "on land"
+WaterLevelZ (0) - Base water level
+DeltaSmoothingFactor (0.1) - Sensitivity of frame rate compensation
+
+May fair winds and following seas guide your journeys across Cyrodiil's waters!
+
+Published by the Imperial Maritime Guild Third Edition, 3E 433
\ No newline at end of file diff --git a/Docs/ModDesc.txt b/Docs/ModDesc.txt new file mode 100644 index 0000000..db8d8a4 --- /dev/null +++ b/Docs/ModDesc.txt @@ -0,0 +1,223 @@ +[size=5][youtube]SE55cqIZNp4[/youtube] + +Features[/size] + +[b]🚣 Movement & Navigation[/b] +[list] +[*]Cast the [b]Row spell[/b] to propel forward (or backward while sneaking) at the cost of fatigue +[*]Intuitive steering - boat automatically turns towards where you looking +[*]Toggle "Lock Heading" to maintain course while you look around freely +[*]Auto-row modes for hands-free forward or backward travel +[*]Multiple boarding options ensure you can always get back on deck +[/list] +[b]🌊 Immersive Physics & Realism[/b] +[list] +[*]Dynamic collision detection prevents merging with terrain and obstacles +[*]Realistic wave simulation rocks the boat based on speed and weather conditions +[*]Player position affects boat balance and tilt +[*]Smooth, frame rate-independent movement ensures consistent performance +[/list] +[b]đŸ—ș Convenience Features[/b] +[list] +[*]Drag the boat overland between bodies of water and it will naturally rest on terrain +[*]Summon your boat from anywhere inside or outside Tamriel +[*]Automatic map marker tracks your boat's location once stationary +[*]Interactive menu system for all boat commands and options +[*]Functional seat for fishing or enjoying the view while anchored +[/list] +[b]đŸ› ïž Purchasable Upgrades[/b] +[list] +[*][b]Storage Chest[/b]: Secure, non-respawning container hidden beneath the seat +[*][b]Lamp[/b]: Toggleable bow-mounted lamp (also toggleable with fire/frost spells) +[*][b]Rope Ladder[/b]: Auto-deploys when you fall overboard for easy re-boarding +[/list] +[b]📚 Documentation & Customization[/b] +[list] +[*]Comprehensive in-game manual written in lore-friendly style +[*]Appendix of console commands for fine-tuning boat behavior +[*]Adjust everything from rowing speed to wave intensity +[*]Full compatibility with both vanilla and modded environments +[/list] +[b]WARNING: this mod is a script-heavy mod. You may experience some performance impact, especially with a weak CPU. This mod is pushing the limits of the script engine. I’ve spent a lot of time to reduce the performance impact and minimize script execution when the boat is not in use. I personally don’t notice an FPS impact but your mileage may vary.[/b] + + +[size=5]Installation[/size] + +[size=4]Requirements[/size] + +[list] +[*][url=https://www.nexusmods.com/oblivionremastered/mods/32]UE4SS[/url]ï»ż: hard requirement. Requirement of UE4SS TESSyncMapInjector. +[*][url=https://www.nexusmods.com/oblivionremastered/mods/1272]UE4SS TesSyncMapInjector[/url]: hard requirement. The rowboat won’t show up in-game without this. +[*][url=https://www.nexusmods.com/oblivionremastered/mods/1966]MagicLoader 2[/url]ï»ż: soft requirement. Fixes the map marker and row spell effect showing up as `` +[*][url=https://www.nexusmods.com/oblivionremastered/mods/282]OBSE64[/url]ï»ż: soft requirement. Needed for [font=Courier New][NL][/font] tag remover. I may use OBSE64 functions in the future as they become available. +[*][url=https://www.nexusmods.com/oblivionremastered/mods/473]NL-Tag Remover[/url]ï»ż: soft requirement. Gets rid of ugly [font=Courier New][NL][/font] prefixes on all the text added by this mod. +[*][url=https://www.nexusmods.com/oblivionremastered/mods/145]Baka Achievement Enabler[/url]ï»ż: optionally recommended. If you plan on tweaking the boat’s behavior it requires use of the console which would disable achievements without this mod. +[/list] +Please double check that you have set up TesSyncMapInjector correctly before posting a bug. + +[size=4]Install +[/size] +Easiest method should be installing using a mod manager like Vortex which should automatically install files in the correct location. + +If you are installing manually: + +[list] +[*]Extract the contents of the ZIP file to [font=Courier New]/OblivionRemastered/Content/Dev/ObvData/Data/[/font] +[*]Ensure that [font=Courier New]RowYourBoat.ini[/font] exists in [font=Courier New]Data/SyncMap[/font] +[*]Ensure that [font=Courier New]RowYourBoat.json[/font] exists in [font=Courier New]Data/MagicLoader[/font] +[*]Ensure that [font=Courier New]RowYourBoat.esp[/font] exists in [font=Courier New]Data/[/font] +[*]Add [font=Courier New]RowYourBoat.esp[/font] to your [font=Courier New]plugins.txt[/font] (also in the Data folder) +[*]Run MagicLoader 2 +[*]Start the game with MagicLoader 2 or OBSE +[/list] + +[size=5]Usage[/size] + +You’ll find the new rowboat right behind the Imperial City Waterfront Shack for sale in the water with a for sale sign posted nearby. + +The boat can be purchased from Sergius Verus at The Three Brothers Trade Goods in the Imperial City Market District for 500 gold. He also sells all three upgrades to the rowboat (Storage Chest, Lamp, and Rope Ladder) for 100 gold each. + +Once the boat is purchased, activate the boat to learn the Row spell and open the boat menu. + +To move the boat you can either select “Auto-row Forward”/“Auto-row Backward” in the boat menu or cast the Row spell while standing on the boat. The Row spell costs 5 fatigue but 0 magicka. Cast it while sneaking to row backwards. Hold down the cast key to continuously move the boat. + +The boat will automatically turn towards the direction you are looking in first-person mode or the direction your character is facing in third-person mode. This can be enabled/disabled with the “Lock Heading”/”Unlock Heading” toggle in the boat menu. + +The boat will automatically detect it is about to collide with land or obstacles and stop the boat. The collision detection waits for 2 seconds at the start of rowing to allow you to clear the boat from any obstacles before starting detection again. + +At any time you can cast the Row spell to summon the boat to your location with two methods: “Summon Boat” which tries to obey terrain and chooses the best location in front of you to place the boat. “Place Boat Right Here” will do exactly that and place the boat right in front of you even if that is inside a hill or floating in-air. It may take a second or two for the boat to appear, especially if you are far away from the boat. + +When the boat is not moving you can select the “Drag Boat” option in the boat menu to drag the boat along with you over land or water. You are pulling all 200 pounds of the boat so be prepared to be encumbered or ready those feather effects. If you would like to stop dragging the boat you can either: activate the boat and select “Stop Dragging", cast the Row spell near the boat and select “Stop Dragging”, drop the Rowboat from your inventory. While dropping the boat from your inventory works, you should try to avoid doing it too much since it creates new references in the game and may lead to save game bloat. + +If purchased, the chest can be used to store loot as you travel by boat. It is marked non-respawning so should be safe to store items long-term. + +If purchased, the lamp can be turned on and off by either: interacting with the lamp, choosing the “Toggle Lamp” option in the boat menu, or casting a fire or frost spell on the lamp. + +If purchased, the rope ladder will deploy when the boat is moving in the water and you fall overboard. Interact with it to sit down on the seat on the boat. Start rowing again to hide the ladder. + +The seat on the boat which is included by default will only be operable when the boat is not moving. + +There’s an in-game manual for the boat. At the end there is an appendix with instructions on how to tweak the boat parameters and a list of all the editable parameters. I've copied that section below here for reference within this spoiler tag: + +[spoiler] +Appendix: Advanced Enchantment Modifications[i] + +The following section is intended only for those versed in the highest arts of maritime enchantment. Improper modification may result in unstable or dangerous vessel behavior. +[/i] +For those brave souls who would tune their vessel's enchantments, speak the ancient console invocation (`) and recite: +[code]ObvConsole set RYB. to [/code] +[b]Movement Enchantments:[/b] +BoatMaxVelocity (6) - Maximum speed attainable +BaseRowForce (0.05) - Force applied when rowing +BaseTurnRate (0.4) - Base turning speed +TurnDeadzone (10) - Degrees before turning begins +VelocityDecayLnRetentionFactor (-0.01005) - Speed depletion rate +TurnRateAcceleration (0.15) - Turn start speed +TurnRateDeceleration (0.92) - Turn stop speed +[b] +Dragging Enchantments:[/b] +DragRopeLength (250) - Distance before boat follows +DragMaxVelocity (4.0) - Maximum dragging speed +DragFriction (0.85) - Resistance when dragging +DragZInterpolationRate (0.1) - Speed of height adjustment +DragBaseTurnRate (0.1) - How quickly boat turns +DragMaxTurnPerFrame (2.0) - Max speed of boat turns +DragTurnDeadzone (5) - Degrees of diversion before boat turns +DragLandPlayerZOffset (30) - Height of boat off ground +DragTurnRateAcceleration (0.15) - Turn start speed +DragTurnRateDeceleration (0.92) - Turn stop speed +DragPathSlopeFactor (0.7) - Tilt angle sensitivity +DragPathSlopeDeadzone (2) - Minimum slope of land to tilt +DragMaxPitchAngle (45) - Maximum tilt angle on slopes +DragPitchSmoothingFactor (0.15) - Smoothness of tilting +DragPathMinDistance (50) - Distance to drag before adjusting tilt +DragUphillZAdjustmentFactor (6) - Amount to lower boat dragging uphill +DragDownhillZAdjustmentFactor (6) - Amount to raise boat dragging downhill + +[b]Wave Simulation Enchantments:[/b] +RockingEnabled (1) - Enable water motion effects +RockAmplitudeZ (0.8) - Vertical rocking magnitude +RockAmplitudePitch (2.0) - Forward/back tilt magnitude +RockAmplitudeRoll (3.0) - Side-to-side tilt magnitude +RockFrequency (30.0) - Primary rocking speed +RockFrequency2 (30.0) - Secondary rocking speed +RockFrequency3 (30.0) - Tertiary rocking (roll) speed +RockWeatherFactor (0.5) - Weather influence multiplier +RockSpeedFactor (0.25) - Boat speed influence multiplier +RockDistanceThreshold (3000) - Distance from boat rocking ceases +RockSmoothingFactor (0.1) - Smoothness of rocking + +[b]Weight Distribution Enchantments:[/b] +PlayerWeightEnabled (1) - Enable weight effects +PlayerWeightPitchFactor (0.15) - Forward/back tilt from position +PlayerWeightRollFactor (0.12) - Side tilt from position +PlayerWeightSmoothingFactor (0.2) - How fast tilt changes +PlayerWeightMaxDistanceForward (240) - Distance lengthwise effect applies +PlayerWeightMaxDistanceSide (100) - Distance sideways effect applies +PlayerWeightMaxDistanceVertical (60) - Distance upwards effect applies + +[b]Collision Detection Enchantments:[/b] +CollisionDetectDelay (2) - Delay before checking obstacles +CollisionDetectZThreshold (10) - Height threshold for collision +ColliderOffset (300) - Distance forward from center for detection +ColliderOffsetReverse (350) - Distance backward from center for detection +ColliderMoveFreq (0.05) - How often to move collider +OverboardDistance (300) - Distance before "overboard" triggers + +[b]Miscellaneous Enchantments:[/b] +SummonDistance (350) - Distance boat appears when summoned +LandZThreshold (10) - Height above water considered "on land" +WaterLevelZ (0) - Base water level +DeltaSmoothingFactor (0.1) - Sensitivity of frame rate compensation + +[i]May fair winds and following seas guide your journeys across Cyrodiil's waters![/i] +[line][i]Published by the Imperial Maritime Guild [/i][i]Third Edition, 3E 433[/i] +[/spoiler] + + +[size=5]Known Issues / FAQ[/size] + +[list] +[*][b]The collision detection sometimes triggers when you are in clear open water.[/b] Sometimes this can happen much more frequently in some problem locations (beneath some bridges) or at very low frame rates. There isn’t much I can do about this, so just try rowing again until you clear the area. +[*][b]The collision detection sometimes fails to trigger when it should.[/b] Particularly when rowing into cliff faces or objects in the water. You’ll likely get pushed overboard if this happens which stops the boat. If the boat gets lost in terrain, you can use the Row spell to summon it back at your location. +[*][b]There’s sometimes a weird splashing sound and visual effect around the boat when it is moving.[/b] That’s just the tiny invisible vampire that is preventing you from losing your boat in the terrain, don’t worry about itÂ ï»żđŸ˜‰ +[*][b]The row spell as a “DO NOT USE - Darkness” effect in addition to the script effect and damage fatigue effect.[/b] This is purely there to remove the annoying spell cast sound and visual effect when you are continuously casting it. It doesn’t actually do anything in the game. +[*][b]The boat is floating above/below the water in interiors.[/b] There’s currently no way to fix this since we can’t query a cell’s water level from scripts. Hopefully we’ll eventually have an OBSE64 function to do this. +[*][b]Sometimes the boat doesn’t appear when summoned.[/b] I noticed there are a few problem locations where this can happen more frequently. If it does, try switching to summoning with the “Place Boat Right Here” option instead. +[*][b]The “Get On Boat” menu option sometimes places you outside the boat.[/b] This seems to be a bug/quirk with the game engine where it just decides to place the player slightly off of the position requested. If this ever happens, use the “Sit Down” option or activate the rope ladder if you have purchased it which doesn’t have this problem. +[*][b]The seat on the boat doesn’t work when the boat is moving.[/b] The seat is intentionally disabled while the boat is moving because the game engine doesn’t allow moving a player that is seated. +[*][b]Followers fall off the boat.[/b] It seems like the game engine is better about keeping the player on the boat than other actors. I’m looking into better solutions for this. +[/list] + +[size=5]Compatibility[/size] + +This mod should be fully compatible most mods. Let me know if you find a mod that isn’t compatible. In the future, this may need patches with any mods that modify: + +[list] +[*]The area behind the IC Waterfront Shack where the rowboat is placed +[*]Sergius Verus or The Three Brothers Trade Goods +[*]Any model replacers for the rowboat, lower-class chest, lower-class stool, ship lamp, or rope ladder. +[*]The Darkness Spell Effect that’s unused in vanilla oblivion +[*]The VampireRace that’s unused in vanilla oblivion +[/list] +This mod was designed specifically with the [url=https://www.nexusmods.com/oblivionremastered/mods/2118]Lived In - Imperial Waterfront mod[/url] in mind, so it is compatible with that. + + +[size=5]Recommended Complementary Mods[/size] + +[list] +[*][url=https://www.nexusmods.com/oblivionremastered/mods/3978]Simply swim up[/url]ï»ż: makes swimming around the boat easier +[*][url=https://www.nexusmods.com/oblivionremastered/mods/2118]Lived In - Imperial Waterfront[/url]ï»ż: fully-compatible, adds a dock for the rowboat +[*][url=https://www.nexusmods.com/oblivionremastered/mods/1790]Eli's Waterfront Shack Overhaul[/url]ï»ż: fully-compatible, spruces up the exterior of the shack +[/list] + +[size=5]Credits[/size] + +[list] +[*]Jason1, Grumblepunk, and Dark Monk for the original inspiration and basis for the scripts in this mod. +[*]Thank you to all the editors of [url=https://cs.uesp.net/wiki/Main_Page]The Elder Scrolls Construction Set Wiki[/url]ï»ż. All the knowledge there was invaluable to this project. +[*]Thanks to everyone that made guides or answered my questions in the [url=https://discord.gg/5tRCpemR]ORMC discord[/url]ï»ż. +[*]Thanks to everyone on the old Bethesda forums and [url=https://tesalliance.org/forums/index.php]TES Alliance[/url]ï»ż for originally helping me learn how to mod Oblivion and putting up with me when I was an annoying teenager all those years ago. +[/list] + + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d98af17 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Tyler Hallada + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/MagicLoader/RowYourBoat.json b/MagicLoader/RowYourBoat.json new file mode 100644 index 0000000..ed38625 --- /dev/null +++ b/MagicLoader/RowYourBoat.json @@ -0,0 +1,6 @@ +{ + "NewStrings": { + "LOC_FN_RYBBoatMapMarkerName": "My Rowboat", + "LOC_FN_RYBRowSpellEffectName": "Row Your Boat" + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2c0648 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Row Your Boat + +Source code and files for the [Row Your Boat Oblivion Remastered +mod](https://www.nexusmods.com/oblivionremastered/mods/4273). + +## Scripts + +For convienence of editing and viewing. All scripts contained within `RowYourBoat.esp` have been extracted out into the +`Scripts` and `ResultScripts` folders. `Scripts` are top-level scripts attached to Objects, Quests, or Spell Effects +within the plugin, while `ResultScripts` are short snippets attached to specific stages in the `RYB` quest. The main +script `RYBQuestScript.psc` got too long and had to be split up, so I put common functions, initialization, and isolated +blocks of code into [Stage Functions](https://cs.uesp.net/wiki/Category:Stage_Functions) on the `RYB` quest. These need +to be kept quite short since result scripts have a small max limit. + +I use the `.psc` extension for better code highlighting, but these technically aren't Papyrus files since these are +OBScript files. + +## ESP + +I'm checking in any changes I make to the `.esp` plugin here, but it's a binary format not easily readable on git +hosting sites. I eventually would like to setup [Spriggit](https://github.com/Mutagen-Modding/Spriggit) for this. + +## Other files + +- `SyncMap`: config for [UE4SS TesSyncMapInjector](https://www.nexusmods.com/oblivionremastered/mods/1272) +- `MagicLoader`: config for [MagicLoader 2](https://www.nexusmods.com/oblivionremastered/mods/1966) +- `Docs`: source for in-game manual and the Nexus Mods mod description \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage10DragAtan2.psc b/ResultScripts/RYBQuestStage10DragAtan2.psc new file mode 100644 index 0000000..a4597d0 --- /dev/null +++ b/ResultScripts/RYBQuestStage10DragAtan2.psc @@ -0,0 +1,25 @@ +; RYB Stage 10 DragTargetAngle atan2 + +; atan2 approximation in radians from https://math.stackexchange.com/a/1105038 +if (RYB.dXAbs >= RYB.dYAbs) + set RYB.a to RYB.dYAbs / RYB.dXAbs +else + set RYB.a to RYB.dXAbs / RYB.dYAbs +endif +set RYB.s to RYB.a * RYB.a +set RYB.r to ((-0.0464964749 * RYB.s + 0.15931422) * RYB.s - 0.327622764) * RYB.s * RYB.a + RYB.a +if (RYB.dYAbs > RYB.dXAbs) + set RYB.r to RYB.halfPi - RYB.r +endif +if (RYB.dX < 0) + set RYB.r to RYB.pi - RYB.r +endif +if (RYB.dY < 0) + set RYB.r to -RYB.r +endif +set RYB.DragTargetAngle to RYB.r * RYB.radToDeg ; radians→deg +; convert mathematical angle → Oblivion heading: heading = (90 – angle) mod 360 +set RYB.DragTargetAngle to 90.0 - RYB.DragTargetAngle +if (RYB.DragTargetAngle < 0) + set RYB.DragTargetAngle to RYB.DragTargetAngle + 360 +endif \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage11UpdateBoatPos.psc b/ResultScripts/RYBQuestStage11UpdateBoatPos.psc new file mode 100644 index 0000000..d38c2b7 --- /dev/null +++ b/ResultScripts/RYBQuestStage11UpdateBoatPos.psc @@ -0,0 +1,13 @@ +; RYB Quest Stage 11 Update Boat Position and Angle + +set RYB.BoatX to RYBBoatRef.GetPos x +set RYB.BoatY to RYBBoatRef.GetPos y +set RYB.BoatZ to RYBBoatRef.GetPos z +set RYB.BoatZ to RYB.BoatZ - RYB.RockZOffset ; remove rocking offset from last frame to get "true" Z +set RYB.BoatAngle to RYBBoatRef.GetAngle z +set RYB.BoatAngle to RYB.BoatAngle - 90 +if (RYB.BoatAngle < 0) + set RYB.BoatAngle to RYB.BoatAngle + 360 +elseif (RYB.BoatAngle >= 360) + set RYB.BoatAngle to RYB.BoatAngle - 360 +endif \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage12BoatYawTrig.psc b/ResultScripts/RYBQuestStage12BoatYawTrig.psc new file mode 100644 index 0000000..387c071 --- /dev/null +++ b/ResultScripts/RYBQuestStage12BoatYawTrig.psc @@ -0,0 +1,16 @@ +; RYB Quest Stage 12 Boat Yaw Angle Trigonometry (sin/cos/tan approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.ang to (RYB.ang * RYB.degToRad) +set RYB.n to 1 +if (RYB.ang > 4.7123) + set RYB.ang to (RYB.ang - 6.2832) +elseif (RYB.ang > 1.5708) + set RYB.ang to (RYB.ang - 3.1416) + set RYB.n to -1 +endif +set RYB.t2 to (RYB.ang * RYB.ang) +set RYB.sin to RYB.n*(RYB.ang*(1 - RYB.t2*0.16605 + 0.00761*RYB.t2*RYB.t2)) +set RYB.cos to RYB.n*(1 - RYB.t2*0.4967 + 0.03705*RYB.t2*RYB.t2) +set RYB.tan to (RYB.sin/RYB.cos) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage13BoatPitchTrig.psc b/ResultScripts/RYBQuestStage13BoatPitchTrig.psc new file mode 100644 index 0000000..74f053b --- /dev/null +++ b/ResultScripts/RYBQuestStage13BoatPitchTrig.psc @@ -0,0 +1,16 @@ +; RYB Quest Stage 13 Boat Pitch Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.PitchRadians to RYB.BoatPitchAngle * RYB.degToRad +set RYB.rockAng to RYB.PitchRadians +set RYB.rockN to 1 +if (RYB.rockAng > 4.7123) + set RYB.rockAng to (RYB.rockAng - 6.2832) +elseif (RYB.rockAng > 1.5708) + set RYB.rockAng to (RYB.rockAng - 3.1416) + set RYB.rockN to -1 +endif +set RYB.rockT2 to (RYB.rockAng * RYB.rockAng) +set RYB.SinPitch to RYB.rockN * (RYB.rockAng * (1 - RYB.rockT2 * 0.16605 + 0.00761 * RYB.rockT2 * RYB.rockT2)) +set RYB.CosPitch to RYB.rockN * (1 - RYB.rockT2 * 0.4967 + 0.03705 * RYB.rockT2 * RYB.rockT2) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage14BoatRollTrig.psc b/ResultScripts/RYBQuestStage14BoatRollTrig.psc new file mode 100644 index 0000000..1b7d3f3 --- /dev/null +++ b/ResultScripts/RYBQuestStage14BoatRollTrig.psc @@ -0,0 +1,16 @@ +; RYB Quest Stage 14 Roll Pitch Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.RollRadians to RYB.BoatRollAngle * RYB.degToRad +set RYB.rockAng to RYB.RollRadians +set RYB.rockN to 1 +if (RYB.rockAng > 4.7123) + set RYB.rockAng to (RYB.rockAng - 6.2832) +elseif (RYB.rockAng > 1.5708) + set RYB.rockAng to (RYB.rockAng - 3.1416) + set RYB.rockN to -1 +endif +set RYB.rockT2 to (RYB.rockAng * RYB.rockAng) +set RYB.SinRoll to RYB.rockN * (RYB.rockAng * (1 - RYB.rockT2 * 0.16605 + 0.00761 * RYB.rockT2 * RYB.rockT2)) +set RYB.CosRoll to RYB.rockN * (1 - RYB.rockT2 * 0.4967 + 0.03705 * RYB.rockT2 * RYB.rockT2) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage15RockPhase1Trig.psc b/ResultScripts/RYBQuestStage15RockPhase1Trig.psc new file mode 100644 index 0000000..3f9ba12 --- /dev/null +++ b/ResultScripts/RYBQuestStage15RockPhase1Trig.psc @@ -0,0 +1,14 @@ +; RYB Quest Stage 15 Rocking Phase 1 (Primary) Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.rockAng to RYB.RockPhase * RYB.degToRad +set RYB.rockN to 1 +if (RYB.rockAng > 4.7123) + set RYB.rockAng to (RYB.rockAng - 6.2832) +elseif (RYB.rockAng > 1.5708) + set RYB.rockAng to (RYB.rockAng - 3.1416) + set RYB.rockN to -1 +endif +set RYB.rockT2 to (RYB.rockAng * RYB.rockAng) +set RYB.rockSin to RYB.rockN * (RYB.rockAng * (1 - RYB.rockT2 * 0.16605 + 0.00761 * RYB.rockT2 * RYB.rockT2)) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage16RockPhase2Trig.psc b/ResultScripts/RYBQuestStage16RockPhase2Trig.psc new file mode 100644 index 0000000..420c791 --- /dev/null +++ b/ResultScripts/RYBQuestStage16RockPhase2Trig.psc @@ -0,0 +1,14 @@ +; RYB Quest Stage 16 Rocking Phase 2 (Secondary) Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.rockAng to RYB.RockPhase2 * RYB.degToRad +set RYB.rockN to 1 +if (RYB.rockAng > 4.7123) + set RYB.rockAng to (RYB.rockAng - 6.2832) +elseif (RYB.rockAng > 1.5708) + set RYB.rockAng to (RYB.rockAng - 3.1416) + set RYB.rockN to -1 +endif +set RYB.rockT2 to (RYB.rockAng * RYB.rockAng) +set RYB.rockSin2 to RYB.rockN * (RYB.rockAng * (1 - RYB.rockT2 * 0.16605 + 0.00761 * RYB.rockT2 * RYB.rockT2)) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage17RockPhase3Trig.psc b/ResultScripts/RYBQuestStage17RockPhase3Trig.psc new file mode 100644 index 0000000..05e9906 --- /dev/null +++ b/ResultScripts/RYBQuestStage17RockPhase3Trig.psc @@ -0,0 +1,15 @@ +; RYB Quest Stage 17 Rocking Phase 3 (Roll) Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.rockAng to RYB.RockPhase3 * RYB.degToRad +set RYB.rockN to 1 +if (RYB.rockAng > 4.7123) + set RYB.rockAng to (RYB.rockAng - 6.2832) +elseif (RYB.rockAng > 1.5708) + set RYB.rockAng to (RYB.rockAng - 3.1416) + set RYB.rockN to -1 +endif +set RYB.rockT2 to (RYB.rockAng * RYB.rockAng) +set RYB.rockSin3 to RYB.rockN * (RYB.rockAng * (1 - RYB.rockT2 * 0.16605 + 0.00761 * RYB.rockT2 * RYB.rockT2)) +set RYB.rockCos3 to RYB.rockN * (1 - RYB.rockT2 * 0.4967 + 0.03705 * RYB.rockT2 * RYB.rockT2) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage18PlayerTrig.psc b/ResultScripts/RYBQuestStage18PlayerTrig.psc new file mode 100644 index 0000000..e8a97c5 --- /dev/null +++ b/ResultScripts/RYBQuestStage18PlayerTrig.psc @@ -0,0 +1,15 @@ +; RYB Quest Stage 18 Player Yaw Angle Trigonometry (sin/cos approximation) + +; Script originally by Galsiah. +; See: https://cs.uesp.net/wiki/Trigonometry_Functions#Galsiah_Version +set RYB.ang to (RYB.ang * RYB.degToRad) +set RYB.n to 1 +if (RYB.ang > 4.7123) + set RYB.ang to (RYB.ang - 6.2832) +elseif (RYB.ang > 1.5708) + set RYB.ang to (RYB.ang - 3.1416) + set RYB.n to -1 +endif +set RYB.t2 to (RYB.ang * RYB.ang) +set RYB.PlayerSin to RYB.n*(RYB.ang*(1 - RYB.t2*0.16605 + 0.00761*RYB.t2*RYB.t2)) +set RYB.PlayerCos to RYB.n*(1 - RYB.t2*0.4967 + 0.03705*RYB.t2*RYB.t2) \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage1InitConstants.psc b/ResultScripts/RYBQuestStage1InitConstants.psc new file mode 100644 index 0000000..63f2bcc --- /dev/null +++ b/ResultScripts/RYBQuestStage1InitConstants.psc @@ -0,0 +1,23 @@ +; RYB Stage 1 Initialize Constants + +set RYB.ModVersion to 0.1 +set RYB.pi to 3.1415927 +set RYB.halfPi to RYB.pi / 2 +set RYB.radToDeg to 180.0/RYB.pi ; always multiply to convert +set RYB.degToRad to 1/RYB.radToDeg +set RYB.TargetDeltaTime to 0.0166 +set RYB.DeltaSmoothingFactor to 0.1 +set RYB.BoatMaxVelocity to 6.0 +set RYB.VelocityDecayLnRetentionFactor to -0.01005 ; approx. natural log of the retention factor of 0.99 +set RYB.BaseTurnRate to 0.4 +set RYB.BaseRowForce to 0.05 +set RYB.TurnDeadzone to 10 +set RYB.RowSpellDuration to 1.5 +set RYB.OverboardDistance to 300 +set RYB.TurnRateAcceleration to 0.15 +set RYB.TurnRateDeceleration to 0.92 +set RYB.LandZThreshold to 10 +set RYB.WaterLevelZ to 0 +set RYB.HighUpdateRate to 0.0166 +set RYB.MediumUpdateRate to 0.1 +set RYB.LowUpdateRate to 0.5 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage20ColliderEnable.psc b/ResultScripts/RYBQuestStage20ColliderEnable.psc new file mode 100644 index 0000000..19cbbbc --- /dev/null +++ b/ResultScripts/RYBQuestStage20ColliderEnable.psc @@ -0,0 +1,15 @@ +; RYB Stage 20 Enable and Position Collider + +RYBColliderRef.Enable +RYBColliderRef.SetActorAlpha 0.0 +RYBColliderRef.SetActorRefraction 10.0 +RYBColliderRef.AddSpell MG14JskarInvis +RYBColliderRef.SetActorValue Aggression 0 +RYBColliderRef.SetActorValue Blindness 100 +RYBColliderRef.ModActorValue Sneak 0 +RYBColliderRef.SetActorsAI 0 +RYBColliderRef.MoveTo Player +RYBColliderRef.SetPos x, RYB.ColliderX +RYBColliderRef.SetPos y, RYB.ColliderY +RYBColliderRef.SetPos z, 0 +RYBColliderRef.SetDestroyed 1 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage21ColliderCollision.psc b/ResultScripts/RYBQuestStage21ColliderCollision.psc new file mode 100644 index 0000000..ba78725 --- /dev/null +++ b/ResultScripts/RYBQuestStage21ColliderCollision.psc @@ -0,0 +1,18 @@ +; RYB Stage 21 Collider Collision + +set RYB.BaseBoatVelocity to 0 +set RYB.CurrentTurnRate to 0 +set RYB.BoatMoving to 0 +set RYB.AutoRowing to 0 +set RYB.Rowing to 0 +RYBColliderRef.Disable +RYBSeatRef.SetDestroyed 0 +RYBBoatMapMarker.Enable +set RYB.CollisionDetectTimer to RYB.CollisionDetectDelay +if (RYB.BaseBoatVelocity > (RYB.BoatMaxVelocity * 0.9) || RYB.BaseBoatVelocity < -(RYB.BoatMaxVelocity * 0.9)) + RYBBoatRef.PlaySound3D TRPImpactStone +else + RYBBoatRef.PlaySound3D TRPMineExplode +endif +set RYB.Grounded to 1 +Message "The boat has grounded." \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage2InitRefs.psc b/ResultScripts/RYBQuestStage2InitRefs.psc new file mode 100644 index 0000000..39891e5 --- /dev/null +++ b/ResultScripts/RYBQuestStage2InitRefs.psc @@ -0,0 +1,19 @@ +; RYB Stage 2 Initialize References + +set RYB.BoatRef to RYBBoatRef +set RYB.Seat to RYBSeatRef +set RYB.SeatForwardOffset to -187 +set RYB.SeatZOffset to 21 +set RYB.Chest to RYBChestRef +set RYB.ChestForwardOffset to -184 +set RYB.ChestZOffset to 14 +set RYB.ChestAngleOffset to -180 +set RYB.BoatMarker to RYBBoatMapMarker +set RYB.BoatMarkerZOffset to 20 +set RYB.Ladder to RYBLadderRef +set RYB.LadderZOffset to -28 +set RYB.LadderForwardOffset to -213 +set RYB.LampLit to RYBLampOnRef +set RYB.LampUnlit to RYBLampOffRef +set RYB.LampForwardOffset to 183 +set RYB.LampZOffset to 86 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage30PositionCalcSeat.psc b/ResultScripts/RYBQuestStage30PositionCalcSeat.psc new file mode 100644 index 0000000..99ecc58 --- /dev/null +++ b/ResultScripts/RYBQuestStage30PositionCalcSeat.psc @@ -0,0 +1,20 @@ +; RYB Stage 30 Seat Position Calculation + +; yaw +set RYB.TempX to RYB.SeatSideOffset * RYB.cos + RYB.SeatForwardOffset * RYB.sin +set RYB.TempY to RYB.SeatForwardOffset * RYB.cos - RYB.SeatSideOffset * RYB.sin +set RYB.TempZ to RYB.SeatZOffset +; roll +set RYB.OrigX to RYB.TempX +set RYB.OrigZ to RYB.TempZ +set RYB.TempX to RYB.OrigX * RYB.CosRoll - RYB.OrigZ * RYB.SinRoll +set RYB.TempZ to RYB.OrigX * RYB.SinRoll + RYB.OrigZ * RYB.CosRoll +; pitch +set RYB.OrigY to RYB.TempY +set RYB.OrigZ to RYB.TempZ +set RYB.TempY to RYB.OrigY * RYB.CosPitch + RYB.OrigZ * RYB.SinPitch +set RYB.TempZ to -RYB.OrigY * RYB.SinPitch + RYB.OrigZ * RYB.CosPitch +; to world coords +set RYB.SeatX to RYB.BoatX + RYB.TempX +set RYB.SeatY to RYB.BoatY + RYB.TempY +set RYB.SeatZ to RYB.BoatZ + RYB.TempZ + RYB.RockZOffset \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage31PositionCalcChest.psc b/ResultScripts/RYBQuestStage31PositionCalcChest.psc new file mode 100644 index 0000000..5cb2791 --- /dev/null +++ b/ResultScripts/RYBQuestStage31PositionCalcChest.psc @@ -0,0 +1,26 @@ +; RYB Stage 31 Chest Position Calculation + +; yaw +set RYB.TempX to RYB.ChestSideOffset * RYB.cos + RYB.ChestForwardOffset * RYB.sin +set RYB.TempY to RYB.ChestForwardOffset * RYB.cos - RYB.ChestSideOffset * RYB.sin +set RYB.TempZ to RYB.ChestZOffset +; roll +set RYB.OrigX to RYB.TempX +set RYB.OrigZ to RYB.TempZ +set RYB.TempX to RYB.OrigX * RYB.CosRoll - RYB.OrigZ * RYB.SinRoll +set RYB.TempZ to RYB.OrigX * RYB.SinRoll + RYB.OrigZ * RYB.CosRoll +; pitch +set RYB.OrigY to RYB.TempY +set RYB.OrigZ to RYB.TempZ +set RYB.TempY to RYB.OrigY * RYB.CosPitch + RYB.OrigZ * RYB.SinPitch +set RYB.TempZ to -RYB.OrigY * RYB.SinPitch + RYB.OrigZ * RYB.CosPitch +; to world coords +set RYB.ChestX to RYB.BoatX + RYB.TempX +set RYB.ChestY to RYB.BoatY + RYB.TempY +set RYB.ChestZ to RYB.BoatZ + RYB.TempZ + RYB.RockZOffset +set RYB.ChestAngle to RYB.BoatAngle - RYB.ChestAngleOffset +if (RYB.ChestAngle < 0) + set RYB.ChestAngle to RYB.ChestAngle + 360 +elseif (RYB.ChestAngle >= 360) + set RYB.ChestAngle to RYB.ChestAngle - 360 +endif \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage32PositionCalcLamp.psc b/ResultScripts/RYBQuestStage32PositionCalcLamp.psc new file mode 100644 index 0000000..04c6cb4 --- /dev/null +++ b/ResultScripts/RYBQuestStage32PositionCalcLamp.psc @@ -0,0 +1,20 @@ +; RYB Stage 32 Lamp Position Calculation + +; yaw +set RYB.TempX to RYB.LampSideOffset * RYB.cos + RYB.LampForwardOffset * RYB.sin +set RYB.TempY to RYB.LampForwardOffset * RYB.cos - RYB.LampSideOffset * RYB.sin +set RYB.TempZ to RYB.LampZOffset +; roll +set RYB.OrigX to RYB.TempX +set RYB.OrigZ to RYB.TempZ +set RYB.TempX to RYB.OrigX * RYB.CosRoll - RYB.OrigZ * RYB.SinRoll +set RYB.TempZ to RYB.OrigX * RYB.SinRoll + RYB.OrigZ * RYB.CosRoll +; pitch +set RYB.OrigY to RYB.TempY +set RYB.OrigZ to RYB.TempZ +set RYB.TempY to RYB.OrigY * RYB.CosPitch + RYB.OrigZ * RYB.SinPitch +set RYB.TempZ to -RYB.OrigY * RYB.SinPitch + RYB.OrigZ * RYB.CosPitch +; to world coords +set RYB.LampX to RYB.BoatX + RYB.TempX +set RYB.LampY to RYB.BoatY + RYB.TempY +set RYB.LampZ to RYB.BoatZ + RYB.TempZ + RYB.RockZOffset \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage33PositionCalcLadder.psc b/ResultScripts/RYBQuestStage33PositionCalcLadder.psc new file mode 100644 index 0000000..3a6d773 --- /dev/null +++ b/ResultScripts/RYBQuestStage33PositionCalcLadder.psc @@ -0,0 +1,20 @@ +; RYB Stage 33 Ladder Position Calculation + +; yaw +set RYB.TempX to RYB.LadderSideOffset * RYB.cos + RYB.LadderForwardOffset * RYB.sin +set RYB.TempY to RYB.LadderForwardOffset * RYB.cos - RYB.LadderSideOffset * RYB.sin +set RYB.TempZ to RYB.LadderZOffset +; roll +set RYB.OrigX to RYB.TempX +set RYB.OrigZ to RYB.TempZ +set RYB.TempX to RYB.OrigX * RYB.CosRoll - RYB.OrigZ * RYB.SinRoll +set RYB.TempZ to RYB.OrigX * RYB.SinRoll + RYB.OrigZ * RYB.CosRoll +; pitch +set RYB.OrigY to RYB.TempY +set RYB.OrigZ to RYB.TempZ +set RYB.TempY to RYB.OrigY * RYB.CosPitch + RYB.OrigZ * RYB.SinPitch +set RYB.TempZ to -RYB.OrigY * RYB.SinPitch + RYB.OrigZ * RYB.CosPitch +; to world coords +set RYB.LadderX to RYB.BoatX + RYB.TempX +set RYB.LadderY to RYB.BoatY + RYB.TempY +set RYB.LadderZ to RYB.BoatZ + RYB.TempZ + RYB.RockZOffset \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage3InitCollider.psc b/ResultScripts/RYBQuestStage3InitCollider.psc new file mode 100644 index 0000000..591f866 --- /dev/null +++ b/ResultScripts/RYBQuestStage3InitCollider.psc @@ -0,0 +1,17 @@ +; RYB Stage 3 Initialize Collider + +set RYB.Collider to RYBColliderRef +set RYB.ColliderOffset to 300 +set RYB.ColliderOffsetReverse to 350 +set RYB.ColliderMoveFreq to 0.05 +set RYB.ColliderZ to 1 +RYBColliderRef.SetActorAlpha 0.0 +RYBColliderRef.SetActorRefraction 10.0 +RYBColliderRef.AddSpell MG14JskarInvis +RYBColliderRef.SetActorValue Aggression 0 +RYBColliderRef.SetActorValue Blindness 100 +RYBColliderRef.ModActorValue Sneak 0 +RYBColliderRef.SetActorsAI 0 +RYBColliderRef.SetDestroyed 1 +set RYB.CollisionDetectDelay to 2 +set RYB.CollisionDetectZThreshold to 10 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage40PositionExecSeat.psc b/ResultScripts/RYBQuestStage40PositionExecSeat.psc new file mode 100644 index 0000000..9b8c804 --- /dev/null +++ b/ResultScripts/RYBQuestStage40PositionExecSeat.psc @@ -0,0 +1,13 @@ +; RYB Stage 40 Seat Position Execution + +if (RYB.Resetting == 2) + RYBSeatRef.MoveTo RYBBoatRef +else + RYBSeatRef.MoveTo Player +endif +RYBSeatRef.SetPos x, RYB.SeatX +RYBSeatRef.SetPos y, RYB.SeatY +RYBSeatRef.SetPos z, RYB.SeatZ +RYBSeatRef.SetAngle z, RYB.BoatAngle +RYBSeatRef.SetAngle x, RYB.BoatPitchAngle +RYBSeatRef.SetAngle y, RYB.BoatRollAngle \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage41PositionExecChest.psc b/ResultScripts/RYBQuestStage41PositionExecChest.psc new file mode 100644 index 0000000..3c0a6b4 --- /dev/null +++ b/ResultScripts/RYBQuestStage41PositionExecChest.psc @@ -0,0 +1,13 @@ +; RYB Stage 41 Chest Position Execution + +if (RYB.Resetting == 2) + RYBChestRef.MoveTo RYBBoatRef +else + RYBChestRef.MoveTo Player +endif +RYBChestRef.SetPos x, RYB.ChestX +RYBChestRef.SetPos y, RYB.ChestY +RYBChestRef.SetPos z, RYB.ChestZ +RYBChestRef.SetAngle z, RYB.ChestAngle +RYBChestRef.SetAngle x, RYB.BoatPitchAngle +RYBChestRef.SetAngle y, RYB.BoatRollAngle \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage42PositionExecLamp.psc b/ResultScripts/RYBQuestStage42PositionExecLamp.psc new file mode 100644 index 0000000..fcaf289 --- /dev/null +++ b/ResultScripts/RYBQuestStage42PositionExecLamp.psc @@ -0,0 +1,26 @@ +; RYB Stage 42 Lamp Position Execution + +if (RYB.Resetting == 2) + RYBLampOffRef.MoveTo RYBBoatRef +else + RYBLampOffRef.MoveTo Player +endif +RYBLampOffRef.SetPos x, RYB.LampX +RYBLampOffRef.SetPos y, RYB.LampY +RYBLampOffRef.SetPos z, RYB.LampZ +RYBLampOffRef.SetAngle z, RYB.BoatAngle +RYBLampOffRef.SetAngle x, RYB.BoatPitchAngle +RYBLampOffRef.SetAngle y, RYB.BoatRollAngle +if (RYB.LampOn == 1) + if (RYB.Resetting == 2) + RYBLampOnRef.MoveTo RYBBoatRef + else + RYBLampOnRef.MoveTo Player + endif + RYBLampOnRef.SetPos x, RYB.LampX + RYBLampOnRef.SetPos y, RYB.LampY + RYBLampOnRef.SetPos z, RYB.LampZ + RYBLampOnRef.SetAngle z, RYB.BoatAngle + RYBLampOnRef.SetAngle x, RYB.BoatPitchAngle + RYBLampOnRef.SetAngle y, RYB.BoatRollAngle +endif \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage43PositionExecLadder.psc b/ResultScripts/RYBQuestStage43PositionExecLadder.psc new file mode 100644 index 0000000..5652b4d --- /dev/null +++ b/ResultScripts/RYBQuestStage43PositionExecLadder.psc @@ -0,0 +1,13 @@ +; RYB Stage 43 Ladder Position Execution + +if (RYB.Resetting == 2) + RYBLadderRef.MoveTo RYBBoatRef +else + RYBLadderRef.MoveTo Player +endif +RYBLadderRef.SetPos x, RYB.LadderX +RYBLadderRef.SetPos y, RYB.LadderY +RYBLadderRef.SetPos z, RYB.LadderZ +RYBLadderRef.SetAngle z, RYB.BoatAngle +RYBLadderRef.SetAngle x, RYB.BoatPitchAngle +RYBLadderRef.SetAngle y, RYB.BoatRollAngle \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage4InitSummoning.psc b/ResultScripts/RYBQuestStage4InitSummoning.psc new file mode 100644 index 0000000..be48877 --- /dev/null +++ b/ResultScripts/RYBQuestStage4InitSummoning.psc @@ -0,0 +1,4 @@ +; RYB Stage 4 Initialize Summoning + +set RYB.SummonDistance to 400 +set RYB.SummonTimerDelay to 0.5 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage5InitDragging.psc b/ResultScripts/RYBQuestStage5InitDragging.psc new file mode 100644 index 0000000..92058e8 --- /dev/null +++ b/ResultScripts/RYBQuestStage5InitDragging.psc @@ -0,0 +1,25 @@ +; RYB Stage 5 Initialize Dragging + +set RYB.DragRopeLength to 250 +set RYB.DragFriction to 0.85 +set RYB.DragBaseTurnRate to 0.1 +set RYB.DragPullStrength to 0.005 +set RYB.DragMaxForce to 0.5 +set RYB.DragMaxVelocity to 4.0 +set RYB.DragZInterpolationRate to 0.1 +set RYB.DragMaxTurnPerFrame to 1.0 +set RYB.DragTurnDeadzone to 5 +set RYB.DragLandPlayerZOffset to 30 +set RYB.DragMaxZPerFrame to 2.0 +set RYB.DragMaxPlayerDistance to RYB.OverboardDistance * 6 +set RYB.DragTurnRateAcceleration to 0.15 +set RYB.DragTurnRateDeceleration to 0.92 +set RYB.DragMaxPitchAngle to 45 +set RYB.DragPitchSmoothingFactor to 0.03 +set RYB.DragPathMinDistance to 50 +set RYB.DragPathSlopeFactor to 0.7 +set RYB.DragPathSlopeDeadzone to 2 +set RYB.DragTargetPitchMovingSmoothingFactor to 0.03 +set RYB.DragTargetPitchStoppedSmoothingFactor to 0.02 +set RYB.DragUphillZAdjustmentFactor to 6 +set RYB.DragDownhillZAdjustmentFactor to 4.5 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage6InitRocking.psc b/ResultScripts/RYBQuestStage6InitRocking.psc new file mode 100644 index 0000000..e5405a6 --- /dev/null +++ b/ResultScripts/RYBQuestStage6InitRocking.psc @@ -0,0 +1,19 @@ +; RYB Stage 6 Initialize Rocking + +set RYB.RockingEnabled to 1 +set RYB.RockAmplitudeZ to 0.8 +set RYB.RockMaxAbsoluteZ to 3.0 +set RYB.RockAmplitudePitch to 2.0 +set RYB.RockAmplitudeRoll to 3.0 +set RYB.RockFrequency to 30.0 +set RYB.RockFrequency2 to 45.0 +set RYB.RockFrequency3 to 25.0 +set RYB.RockRandomInterval to 3.0 +set RYB.RockSpeedFactor to 0.25 +set RYB.RockDistanceThreshold to 3000 +set RYB.RockSmoothingFactor to 0.1 +set RYB.RockWeatherFactor to 0.5 +set RYB.RockPhase to GetRandomPercent * 3.6 +set RYB.RockPhase2 to GetRandomPercent * 3.6 +set RYB.RockPhase3 to GetRandomPercent * 3.6 +set RYB.RockRandomPhase to (GetRandomPercent - 50) * 0.02 \ No newline at end of file diff --git a/ResultScripts/RYBQuestStage7InitPlayerWeight.psc b/ResultScripts/RYBQuestStage7InitPlayerWeight.psc new file mode 100644 index 0000000..22b248e --- /dev/null +++ b/ResultScripts/RYBQuestStage7InitPlayerWeight.psc @@ -0,0 +1,9 @@ +; RYB Stage 7 Initialize Player Weight + +set RYB.PlayerWeightEnabled to 1 +set RYB.PlayerWeightMaxDistanceForward to 240 +set RYB.PlayerWeightMaxDistanceSide to 100 +set RYB.PlayerWeightMaxDistanceVertical to 60 +set RYB.PlayerWeightPitchFactor to 0.07 ; How much forward/back position affects pitch +set RYB.PlayerWeightRollFactor to 0.13 ; How much left/right position affects roll +set RYB.PlayerWeightSmoothingFactor to 0.2 ; How quickly the effect changes \ No newline at end of file diff --git a/RowYourBoat.esp b/RowYourBoat.esp new file mode 100644 index 0000000..da1fb6f Binary files /dev/null and b/RowYourBoat.esp differ diff --git a/Scripts/RYBBoatReceiptScript.psc b/Scripts/RYBBoatReceiptScript.psc new file mode 100644 index 0000000..fbf5d5b --- /dev/null +++ b/Scripts/RYBBoatReceiptScript.psc @@ -0,0 +1,14 @@ +ScriptName RYBBoatReceiptScript + +short DoOnce + +begin OnAdd + if (DoOnce == 0) + SetItemValue 0 + set RYB.BoatPurchased to 1 + RYBForSaleSignRef.Disable + RYBBoatMapMarker.Enable + ShowMap RYBBoatMapMarker 1 + set DoOnce to 1 + endif +end \ No newline at end of file diff --git a/Scripts/RYBBoatScript.psc b/Scripts/RYBBoatScript.psc new file mode 100644 index 0000000..d1d0eee --- /dev/null +++ b/Scripts/RYBBoatScript.psc @@ -0,0 +1,9 @@ +ScriptName RYBBoatScript + +begin OnActivate + if (RYB.BoatPurchased == 0) + MessageBox "There's a note attached to the rowboat that reads: 'Contact Sergius Verus at the Three Brothers Trade Goods in the Market District to purchase this boat.'" "Ok" + else + set RYBMenu.BoatActivated to 1 + endif +end \ No newline at end of file diff --git a/Scripts/RYBBoatTokenScript.psc b/Scripts/RYBBoatTokenScript.psc new file mode 100644 index 0000000..57a7a8e --- /dev/null +++ b/Scripts/RYBBoatTokenScript.psc @@ -0,0 +1,13 @@ +ScriptName RYBBoatTokenScript + +begin OnDrop Player + Disable + PositionCell 0, 0, 0, 0, RYBCell + ResetInterior RYBCell ; don't think this deletes the reference but I have no other way to do it + set RYB.TriggerStopDragging to 1 +end + +begin OnSell Player + MessageBox "Gasp! How could you sell me? I thought we were friends!" + set RYB.TriggerStopDragging to 1 +end \ No newline at end of file diff --git a/Scripts/RYBChestReceiptScript.psc b/Scripts/RYBChestReceiptScript.psc new file mode 100644 index 0000000..5d1e5c1 --- /dev/null +++ b/Scripts/RYBChestReceiptScript.psc @@ -0,0 +1,13 @@ +ScriptName RYBChestReceiptScript + +short DoOnce + +begin OnAdd + if (DoOnce == 0) + SetItemValue 0 + set RYB.ChestPurchased to 1 + RYBChestRef.Enable + set RYB.Resetting to 2 ; trigger one-time boat position reset to make sure the chest is placed on the boat + set DoOnce to 1 + endif +end \ No newline at end of file diff --git a/Scripts/RYBLadderReceiptScript.psc b/Scripts/RYBLadderReceiptScript.psc new file mode 100644 index 0000000..7f6dbc9 --- /dev/null +++ b/Scripts/RYBLadderReceiptScript.psc @@ -0,0 +1,12 @@ +ScriptName RYBLadderReceiptScript + +short DoOnce + +begin OnAdd + if (DoOnce == 0) + SetItemValue 0 + set RYB.LadderPurchased to 1 + set RYB.Resetting to 2 ; trigger one-time boat position reset to make sure the ladder is placed on the boat + set DoOnce to 1 + endif +end \ No newline at end of file diff --git a/Scripts/RYBLadderScript.psc b/Scripts/RYBLadderScript.psc new file mode 100644 index 0000000..f53058c --- /dev/null +++ b/Scripts/RYBLadderScript.psc @@ -0,0 +1,5 @@ +ScriptName RYBLadderScript + +begin OnActivate + set RYB.TriggerGetOnBoat to 2 +end \ No newline at end of file diff --git a/Scripts/RYBLampReceiptScript.psc b/Scripts/RYBLampReceiptScript.psc new file mode 100644 index 0000000..90d867f --- /dev/null +++ b/Scripts/RYBLampReceiptScript.psc @@ -0,0 +1,13 @@ +ScriptName RYBLampReceiptScript + +short DoOnce + +begin OnAdd + if (DoOnce == 0) + SetItemValue 0 + set RYB.LampPurchased to 1 + RYBLampOffRef.Enable + set RYB.Resetting to 2 ; trigger one-time boat position reset to make sure the lamp is placed on the boat + set DoOnce to 1 + endif +end \ No newline at end of file diff --git a/Scripts/RYBLampScript.psc b/Scripts/RYBLampScript.psc new file mode 100644 index 0000000..6dbe40a --- /dev/null +++ b/Scripts/RYBLampScript.psc @@ -0,0 +1,17 @@ +ScriptName RYBLampScript + +begin OnActivate + if (RYB.LampOn == 0) + set RYB.LampOn to 1 + else + set RYB.LampOn to 0 + endif +end + +begin OnMagicEffectHit FIDG + set RYB.LampOn to 1 +end + +begin OnMagicEffectHit FRDG + set RYB.LampOn to 0 +end \ No newline at end of file diff --git a/Scripts/RYBMenuScript.psc b/Scripts/RYBMenuScript.psc new file mode 100644 index 0000000..2efd3fe --- /dev/null +++ b/Scripts/RYBMenuScript.psc @@ -0,0 +1,235 @@ +ScriptName RYBMenuScript + +float fQuestDelayTime ; controls script processing rate, see: https://cs.uesp.net/wiki/FQuestDelayTime + +; Quest script states +short Initializing + +; MessageBox states +short Choice +short ChoosingInit +short ChoosingEnroute +short ChoosingSummon +short ChoosingDrag +short ChoosingOnLand + +short ChoosingSettings +short SettingDragMaxPitchAngle +short SettingDragPitchSmoothingFactor + +; Triggers +short BoatActivated ; player activating the boat will set this to 1 and this script will handle it +; set after RYB handles player casting the row spell +; 3 to show not on boat messagebox, 4 = show dragging messagebox, 5 = show on land messagebox +short TriggerRowCast + +begin GameMode + if (Initializing == 0) + Set Initializing to 1 + set fQuestDelayTime to RYB.LowUpdateRate + endif + if (RYB.BoatPurchased == 0) + set fQuestDelayTime to RYB.MediumUpdateRate ; for faster menu responsiveness + endif + + if (BoatActivated == 1) + set BoatActivated to 0 + Player.AddSpell RYBRowSpell + if (RYB.BoatMoving >= 1) + set ChoosingEnroute to 1 + if (RYB.AutoRowing == 0 && RYB.LockHeading == 0) + MessageBox "Boat is in motion.", "Auto-row Forward", "Auto-row Backward", "Lock Heading", "Toggle Lamp", "Cancel" + elseif (RYB.AutoRowing == 0 && RYB.LockHeading == 1) + MessageBox "Boat is in motion with heading locked.", "Auto-row Forward", "Auto-row Backward", "Unlock Heading", "Toggle Lamp", "Cancel" + elseif (RYB.AutoRowing == 1 && RYB.LockHeading == 0) + MessageBox "Auto-rowing forwards", "Stop Auto-rowing", "Auto-row Backward", "Lock Heading", "Toggle Lamp", "Cancel" + elseif (RYB.AutoRowing == 1 && RYB.LockHeading == 1) + MessageBox "Auto-rowing forwards with heading locked", "Stop Auto-rowing", "Auto-row Forward", "Unlock Heading", "Toggle Lamp", "Cancel" + elseif (RYB.AutoRowing == 2 && RYB.LockHeading == 0) + MessageBox "Auto-rowing backwards", "Stop Auto-rowing", "Auto-row Forward", "Lock Heading", "Toggle Lamp", "Cancel" + elseif (RYB.AutoRowing == 2 && RYB.LockHeading == 1) + MessageBox "Auto-rowing backwards with heading locked", "Stop Auto-rowing", "Auto-row Forward", "Unlock Heading", "Toggle Lamp", "Cancel" + endif + set Choice to GetButtonPressed + elseif (RYB.Dragging >= 1) + set ChoosingDrag to 1 + MessageBox "Boat is being dragged.", "Stop Dragging", "Cancel" + set Choice to GetButtonPressed + elseif (RYB.OnLand == 0) + set ChoosingInit to 1 + MessageBox "Cast Row spell or start auto rowing to start moving.", "Auto-row Forward", "Auto-row Backward", "Get On Boat", "Sit Down", "Toggle Lamp", "Drag Boat", "Settings", "Read Manual", "Cancel" + set Choice to GetButtonPressed + else + set ChoosingOnLand to 1 + MessageBox "Boat is on land. Drag it into the water?", "Drag Boat", "Get On Boat", "Sit Down", "Toggle Lamp", "Settings", "Read Manual", "Cancel" + set Choice to GetButtonPressed + endif + endif + + if (ChoosingInit == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingInit to 0 + set RYB.TriggerAutoRow to 1 + elseif (Choice == 1) + set ChoosingInit to 0 + set RYB.TriggerAutoRow to 2 + elseif (Choice == 2) + set ChoosingInit to 0 + set RYB.TriggerGetOnBoat to 1 + elseif (Choice == 3) + set ChoosingInit to 0 + set RYB.TriggerGetOnBoat to 2 + elseif (Choice == 4) + set ChoosingInit to 0 + if (RYB.LampOn == 0) + set RYB.LampOn to 1 + else + set RYB.LampOn to 0 + endif + elseif (Choice == 5) + set ChoosingInit to 0 + set RYB.TriggerStartDragging to 1 + elseif (Choice == 6) + set ChoosingInit to 0 + set ChoosingSettings to 1 + MessageBox "Settings", "Toggle Rocking", "Toggle Player Weight", "Cancel" + set Choice to GetButtonPressed + elseif (Choice == 7) + set ChoosingInit to 0 + RYBManualRef.Activate Player + elseif (Choice == 8) + set ChoosingInit to 0 + endif + endif + + if (ChoosingEnroute == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingEnroute to 0 + if (RYB.AutoRowing == 0) + set RYB.AutoRowing to 1 + else + set RYB.AutoRowing to 0 + endif + elseif (Choice == 1) + set ChoosingEnroute to 0 + if (RYB.AutoRowing == 1) + set RYB.AutoRowing to 2 + elseif (RYB.AutoRowing == 2) + set RYB.AutoRowing to 1 + endif + elseif (Choice == 2) + set ChoosingEnroute to 0 + if (RYB.LockHeading == 0) + set RYB.LockHeading to 1 + Message "Boat will maintain present heading." + else + set RYB.LockHeading to 0 + Message "Boat can now turn." + endif + elseif (Choice == 3) + set ChoosingEnroute to 0 + if (RYB.LampOn == 0) + set RYB.LampOn to 1 + else + set RYB.LampOn to 0 + endif + elseif (Choice == 4) + set ChoosingEnroute to 0 + endif + endif + + if (ChoosingSummon == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingSummon to 0 + set RYB.TriggerSummonBoat to 1 + elseif (Choice == 1) + set ChoosingSummon to 0 + set RYB.TriggerSummonBoat to 2 + elseif (Choice == 2) + set ChoosingSummon to 0 + endif + endif + + if (ChoosingDrag == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingDrag to 0 + set RYB.TriggerStopDragging to 1 + Message "You stop dragging the boat." + elseif (Choice == 1) + set ChoosingDrag to 0 + endif + endif + + if (ChoosingOnLand == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingOnLand to 0 + set RYB.TriggerStartDragging to 1 + elseif (Choice == 1) + set ChoosingOnLand to 0 + set RYB.TriggerGetOnBoat to 1 + elseif (Choice == 2) + set ChoosingOnLand to 0 + set RYB.TriggerGetOnBoat to 2 + elseif (Choice == 3) + set ChoosingOnLand to 0 + if (RYB.LampOn == 0) + set RYB.LampOn to 1 + else + set RYB.LampOn to 0 + endif + elseif (Choice == 4) + set ChoosingOnLand to 0 + set ChoosingSettings to 1 + MessageBox "Settings", "Toggle Rocking", "Toggle Player Weight", "Cancel" + set Choice to GetButtonPressed + elseif (Choice == 5) + set ChoosingOnLand to 0 + RYBManualRef.Activate Player + elseif (Choice == 6) + set ChoosingOnLand to 0 + endif + endif + + if (TriggerRowCast == 3) + set TriggerRowCast to 0 + MessageBox "You're not on the boat." "Summon Boat" "Place Boat Right Here" "Cancel" + set Choice to GetButtonPressed + set ChoosingSummon to 1 + elseif (TriggerRowCast == 4) + set TriggerRowCast to 0 + set ChoosingDrag to 1 + MessageBox "Boat is being dragged.", "Stop Dragging", "Cancel" + set Choice to GetButtonPressed + elseif (TriggerRowCast == 5) + set TriggerRowCast to 0 + set ChoosingOnLand to 1 + MessageBox "Boat is on land. Drag it into the water?", "Drag Boat", "Get On Boat", "Sit Down", "Toggle Lamp", "Settings", "Cancel" + set Choice to GetButtonPressed + endif + + if (ChoosingSettings == 1) + set Choice to GetButtonPressed + if (Choice == 0) + set ChoosingSettings to 0 + if (RYB.RockingEnabled == 0) + set RYB.RockingEnabled to 1 + else + set RYB.RockingEnabled to 0 + endif + elseif (Choice == 1) + set ChoosingSettings to 0 + if (RYB.PlayerWeightEnabled == 0) + set RYB.PlayerWeightEnabled to 1 + else + set RYB.PlayerWeightEnabled to 0 + endif + elseif (Choice == 2) + set ChoosingSettings to 0 + endif + endif +end \ No newline at end of file diff --git a/Scripts/RYBQuestScript.psc b/Scripts/RYBQuestScript.psc new file mode 100644 index 0000000..4f17bdc --- /dev/null +++ b/Scripts/RYBQuestScript.psc @@ -0,0 +1,1300 @@ +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 +short Resetting ; 1 = run positioning code once relative to player, 2 = run positioning code once relative to boat +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 + +; 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 ColliderY +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 + +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 + + 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 + else + set fQuestDelayTime to LowUpdateRate ; low processing rate when far 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 + BoatRef.Disable + BoatMarker.Disable + Seat.Disable + Chest.Disable + LampLit.Disable + LampUnlit.Disable + Ladder.Disable + 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 + BoatRef.Disable + BoatMarker.Disable + Seat.Disable + Chest.Disable + LampLit.Disable + LampUnlit.Disable + Ladder.Disable + 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 + BoatRef.Enable + BoatMarker.Enable + Seat.Enable + if (ChestPurchased == 1) + Chest.Enable + endif + if (LampPurchased == 1) + if (LampOn == 1) + LampLit.Enable + endif + LampUnlit.Enable + endif + if (LadderPurchased == 1 && LadderDeployed == 1) + Ladder.Enable + endif + if (RockingEnabled == 0) + set fQuestDelayTime to MediumUpdateRate + endif + set Resetting to 1 + endif + + if (LampOn == 1 && LampLit.GetDisabled == 1) + set Resetting to 1 + LampLit.Enable + LampUnlit.PlaySound3D SPLFireballFail + elseif (LampOn == 0 && LampLit.GetDisabled == 0) + 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) + if (RockingEnabled == 0) + set fQuestDelayTime to MediumUpdateRate + endif + SetStage RYB 21 ; Collision procedure + 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 && PlayerDistance < RockDistanceThreshold) + ; 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 || (PlayerDistance < RockDistanceThreshold && (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 || (PlayerDistance < RockDistanceThreshold && (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 && (PlayerDistance < RockDistanceThreshold && (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 && (PlayerDistance < RockDistanceThreshold && (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 \ No newline at end of file diff --git a/Scripts/RYBRowSpellScript.psc b/Scripts/RYBRowSpellScript.psc new file mode 100644 index 0000000..ccab287 --- /dev/null +++ b/Scripts/RYBRowSpellScript.psc @@ -0,0 +1,9 @@ +ScriptName RYBRowSpellScript + +begin ScriptEffectStart + if (Player.IsSneaking) + set RYB.TriggerRowCast to 2 + else + set RYB.TriggerRowCast to 1 + endif +end \ No newline at end of file diff --git a/SyncMap/RowYourBoat.ini b/SyncMap/RowYourBoat.ini new file mode 100644 index 0000000..ce319bb --- /dev/null +++ b/SyncMap/RowYourBoat.ini @@ -0,0 +1,16 @@ +[Meshes] +000ed3=/Game/Forms/worldobjects/activator/MS08Rowboat.MS08Rowboat +0030fb=/Game/Forms/magic/spell/StandardShockDamageTouch1Novice.StandardShockDamageTouch1Novice +003eeb=/Game/Forms/actors/npc/TESTMarauder.TESTMarauder +005450=/Game/Forms/worldobjects/container/PCChestClutterLower01.PCChestClutterLower01 +005b3a=/Game/Forms/worldobjects/activator/ShipLamp01.ShipLamp01 +006920=/Game/Forms/worldobjects/door/RopeLadder01.RopeLadder01 +00692b=/Game/Forms/worldobjects/activator/MS08Rowboat.MS08Rowboat +007dbd=/Game/Forms/worldobjects/light/ShipLamp300.ShipLamp300 +008497=/Game/Forms/worldobjects/container/PCChestClutterLower01.PCChestClutterLower01 +008493=/Game/Forms/items/book/HouseICStorageAreaReceipt.HouseICStorageAreaReceipt +008494=/Game/Forms/items/book/HouseICStorageAreaReceipt.HouseICStorageAreaReceipt +008495=/Game/Forms/items/book/HouseICStorageAreaReceipt.HouseICStorageAreaReceipt +008496=/Game/Forms/items/book/HouseICStorageAreaReceipt.HouseICStorageAreaReceipt +008b70=/Game/Forms/items/book/Book2CommonManualArmor.Book2CommonManualArmor +008b72=/Game/Forms/items/book/Book2CommonManualArmor.Book2CommonManualArmor \ No newline at end of file