Bathing in Skyrim 1.10 is out and I wrote a new way to handle water detection that I thought I’d share.
For some background – something that has been a problem with many of the basic-needs mods for Skyrim is that the CreationKit does not expose a reliable way to detect if an Actor is standing in water.
Early on, someone discovered that there is something in the CreationKit that lets you mark the water as “dangerous water” along with a float that represents the amount of damage per second received while standing in it (which can be set to 0). This is helpful since Papyrus and the CreationKit have an exposed method named IsInDangerousWater that will detect an Actor in dangerous water even if they just dip their toe into it. This is how mods like Realistic Needs and Diseases detects water.
A problem comes up, though, if a mod makes additional edits to WaterTypes. Like all Forms, the changes made last will win any conflict. That means if you load a mod like Realistic Water Two (that makes changes to water) after Realistic Needs and Diseases, the “dangerous water” flag will be set to false and water detection breaks. If you load them in reverse order, water detection works but the water will look like vanilla water.
To fix this problem, people started making “patches” that are ESP files existing solely to set the “dangerous water” flag to true in a way that merges the change with the changes from the water mod. This is how Bathing in Skyrim detected water until 1.10.
This method is fine, but it requires mod authors to write patches for every DLC and water mod combination that users want to use. It’s not complicated but it is tedious. It also requires users to activate several ESPs in their load order for a single mod. Since people have recently been hitting the 255 ESP cap, this presents a problem.
A few months ago, two of the three biggest water mods decided to bundle their mods with other mods and change the ESP names. This made all of my patches worthless. I did not want to re-patch all the water mods/DLC again, so I looked for alternatives.
In a recent release of SKSE, there is a new exposed method of getting the height of the water level in a cell. This is how iNeed started detecting water recently. The idea is that if you know the water level of an area and test it against the player’s position you can tell if they are standing below the water level and therefore standing in water. Theoretically, this solves all the problems with patching and lets people use any water mod/DLC combinations they want.
I tried implementing this method of water detection with Bathing in Skyrim 1.05 but I ran into problems and switched back to patching. My main issue was that I could not reliably detect water in areas that had stepped water levels – for instance, the stairway up to Dragonsreach in Whiterun. The water level reported in the cells was the same even though the water was placed at different heights, breaking the detection in those areas. So the trade off is that I could do less work and support more water mods, but some areas would just be off-limits. I decided not to go this route since I believe it’s a worse overall experience for the user.
More recently I became interested in writing my own SKSE plugins. I was originally playing around with camera controls and thought I might try to find some automatic SKSE way of setting the water to “dangerous water.” I managed to do just that, allowing Bathing in Skyrim 1.10 to take advantage of the new method.
In mzinWaterUtil.dll I expose two Papyrus methods for water:
Bool Function IsDangerousWater(Form WaterForm) Global Native
Function SetDangerousWater(Form WaterForm, Bool Dangerous) Global Native
The first tells you if the “dangerous water” flag is checked or not on a given Form. The second will set the flag to true or false based on the Dangerous parameter.
This is what the SKSE code looks like:
bool mzin::IsDangerousWater(StaticFunctionTag* base, TESForm* waterForm)
if (waterForm->GetFormType() != kFormType_Water)
return (dynamic_cast<TESWaterForm*>(waterForm)->unk075 & DangerousWaterFlagMask) == DangerousWaterFlagMask;
void mzin::SetDangerousWater(StaticFunctionTag* base, TESForm* waterForm, bool dangerous)
if (waterForm->GetFormType() != kFormType_Water)
dynamic_cast<TESWaterForm*>(waterForm)->unk075 |= DangerousWaterFlagMask;
dynamic_cast<TESWaterForm*>(waterForm)->unk075 &= ~DangerousWaterFlagMask;
I found that the unknown value “unk075” represents the “is dangerous” flag by dumping all of the data out of a WaterType and comparing a “patched” version vs a vanilla version. I found that unk075 changes from 0 (false) to 1 (true) when patched. unk075 is a UInt8 and I’m not sure if any other flags might get stored there (with bit-sets or whatever) so instead of just hard setting it to 0 or 1, I decided to use bit-wise operations with this bit-mask:
static const UInt8 DangerousWaterFlagMask = 0x01;
This might be total overkill, but it should produce the same results as water patching when checking the “dangerous water” box in maybe a safer way then setting the UInt8 directly as 0 or 1. I’m also checking that the passed Form is a WaterType before doing any operations since Papyrus has no exposed WaterType Form, making it unenforceable as a type in the Papyrus script itself.
So now there is a way to mark the dangerous water flag dynamically in Papyrus, but it needs to know the Form IDs to do it. So, I created a FormList in the CreationKit and placed all the vanilla Skyrim WaterType Forms inside which gets processed by a script at startup.
This is what the Papyrus code looks like to mark the water:
Int Index = WaterList.GetSize()
Bool Dangerous = WaterRestrictionEnabled.GetValue() As Bool
Index -= 1
Form WaterForm = WaterList.GetAt(Index)
If WaterForm != None
Pretty simple – it just iterates through the FormList and uses SKSE to set the flag.
For Dragonborn and Dawnguard I just wrote down the Form IDs to add them to the list dynamically. This happens just before processing the FormList:
If Game.GetModByName("Dragonborn.esm") != 255
Debug.Notification("Bathing in Skyrim - Dragonborn Support Added")
Game.GetModByName() is awesome since it lets me know if someone has a mod loaded or not. If it returns 255 then the mod is not loaded and I won’t try to add the WaterTypes to the FormList. Since DLC and user-made mods are treated the same, I have a similar block for Falskaar and I can add new ones whenever new user-made mods add new WaterTypes. Mod authors could also write their own methods for this since it’s all in Papyrus.
The changes to the Forms do not stick through a save, so they must be reapplied every time a user loads the game. To do this, in the main Bathing in Skyrim Quest I added a ReferenceAlias for the Player and call this:
Which calls the Quest to check for DLC and process the FormList of WaterTypes.
I’ve been trying to get this same behavior contained within the mzinWaterUtil DLL entirely so that once the game loads it will just automatically patch all the water without the need for Papyrus, but so far I’ve been unsuccessful. Hopefully it’s something I can add in the future so players can just drop the DLL in a folder and magically get patched water for Bathing in Skyrim, Realistic Needs and Diseases, etc without the need for a single water patch ESP regardless of water mods or DLC installed, but so far no luck.
The full source for the Papyrus is available on Git here: