Things I Learned - DeviousFramework/SkyrimDeviousFramework GitHub Wiki

Mod Dependencies - Removing

Recently I had to remove Dawnguard.esm and Dragonborn.esm as master file dependencies for a mod since I do not have them.
Here are the steps I took to do so:

  1. If you do not have them copy Skyrim.esm to Dawnguard.esm and Dragonborn.esm in "Data".
  2. In TES5Edit right click on the mod and select "Check for Errors".
    Investigate any errors marked as "Could not be resolved".
    These may indicate the mod actually is dependent on those files.
  3. Open the Creation Kit.
  4. Select the "Open Data" button normally used to open a plugin.
  5. Select the mod's .esp from the list. The window on the right will show the "Parent Masters".
  6. Select Dawnguard in the "Parent Masters" list and used Ctrl-Delete to remove it as a Master.
  7. Repeat this for Dragonborn.
  8. Remember to remove the fake Dawnguard and Dragonborn .esm files.
    If you have the real Dawnguard and Dragonborn master (.esm) files, instead of step 2 you can use the "Apply Script" => "Report Masters" feature of TESVEdit to identify if they are used.

Threading and Mutexes

I planned to add a set of mutex functions to the Devious Framework (DFW) utility script; however, I have since learned accessing external scripts (i.e. the DFW utility script) behaves more or less like a remote function call, releasing the lock execution of the current thread.
More information can be found on the Papyrus Threading Notes web page.
Script instances are generally thread safe. No functions or events can run and no properties can be accessed concurrently. However, there is still a need for mutexes. While code cannot run concurrently a function may begin to execute and then perform a remote function call, allowing other code in the script to run before the function returns to what is was doing.
Note that even functions such as Debug.Trace() will potentially unlock thread execution.
If you have a need for a section of code to run to completion without other code interrupting it use mutexes. I will not add mutex code to DFW utility script as it does not make sense to unlock thread execution in order to lock thread execution. In general thread execution should be unlocked as infrequently as possible. You can add these functions to a private function section of your own script and use them.
If adding to an already started game you will need to initialize global variables in the script's upgrade section.

; A set of variables to manage mutexes.
Int[] _aiMutex
String[] _aszMutexName
Int _iMutexNext

; A reference to the Devious Framework Util quest script.
dfwUtil _qDfwUtil

Event OnInit()
   ; Create an initial mutex for protecting the mutex list.
   _aiMutex      = New Int[1]
   _aszMutexName = New String[1]
   _aiMutex[0]      = 0
   _aszMutexName[0] = "Mutex List Mutex"
   _iMutexNext      = 1

   _qDfwUtil = (Self As Quest) As dfwUtil
EndEvent

Int Function iMutexCreate(String szName, Int iTimeoutMs=1000)
   Int iIndex = -1

   ; Lock the mutex protecting the mutex list to protect creating two at once.
   ; AddXxxToArray() can unlock the thread allowing this function to run again.
   If (MutexLock(0, iTimeoutMs))
      iIndex = _aszMutexName.Find(szName)
      If (0 <= iIndex)
         ; This mutex already exists clear it.
         _aiMutex[iIndex] = 0
      Else
         ; Otherwise create a new mutex entry.
         _aiMutex      = _qDfwUtil.AddIntToArray(_aiMutex, 0)
         _aszMutexName = _qDfwUtil.AddStringToArray(_aszMutexName, szName)
         iIndex = _iMutexNext
         _iMutexNext += 1
      EndIf

      ; Release the mutex protecting the mutex list.
      MutexRelease(0)
   EndIf
   Return iIndex
EndFunction

Bool Function MutexLock(Int iMutex, Int iTimeoutMs=1000)
   ; If this is not a valid mutex return a failure.
   If ((0 > iMutex) || (_iMutexNext <= iMutex))
      Return False
   EndIf

   Bool bUseTimeout = (iTimeoutMs As Bool)
   While (0 < _aiMutex[iMutex])
      Utility.Wait(0.1)
      If (bUseTimeout)
         iTimeoutMs -= 100
         If (0 >= iTimeoutMs)
            ; Locking Failed due to timeout.
            Return False
         EndIf
      EndIf
   EndWhile
   ; Locking succeeded.  Increment the mutex and return success.
   _aiMutex[iMutex] = _aiMutex[iMutex] + 1
   Return True
EndFunction

Function MutexRelease(Int iMutex)
   ; If this is a valid mutex decrement it.
   If ((0 <= iMutex) && (_iMutexNext > iMutex))
      _aiMutex[iMutex] = _aiMutex[iMutex] - 1
   EndIf
EndFunction

ZaZ Player Slavery

The ZaZ Animation Pack has a number of features that help co-ordinate mods keeping track of player slavery. There are three that I have learned about and I will describe them in this section with sample code. Whether there are more or not, I am not sure.
Note: From what I understand there are two main ZaZ Animation Pack files for working with the player as a slave, zbfSlaveControl.psc, and zbfSlaveActions.psc. The slave control file focuses on identifying slaves and masters while the slave action file is more related to animations and applying restraints and devices.
Note: If using the Devious Framework mod it will attempt to perform this registration automatically.
There are also some more complicated functions for "borrowing", "returning", "pausing", and "resuming" the slave; however, I am not as confident in describing those functions and will not try to.

Marking the Player as a Slave

The zbfSlaveControl has a faction for identifying slaves:

; Please use the api functions to detect the actual state of the slave.
; (For rationale, see http://www.creationkit.com/GetFactionRank_-_Actor)
;
; - -1: Actor is an escaped slave.
; -  0: Actor is a basic slave.
; -  1: Actor is a friend to his/her master.
; -  2: Actor is a confidant to his/her master.
; -  3: Actor is an ally to his/her master.
; -  4: Actor is a lover to his/her master.
;
; Increasing slave rank has no immediate implications, but these levels indicate how
; slavery can progress from involuntary (0) to voluntary (4). These serve as an
; alternative to relationship ranks, which do not persist across saves.
;
Faction Property zbfFactionSlave Auto

While checking the faction rank can use the generic faction functions, for marking the player (or another actor) as a slave an API function is provided.
There are also some more complicated functions for "borrowing", "returning", "pausing", and "resuming" the slave; however, I am not as confident in describing those functions and will not try to.

Identifying Slave Owners and Traders

The zbfSlaveControl has a few factions for identifying slave owners and traders:

; Indicates that the actor is a master to a slave.
;
; The system does not check any inconsistencies in detecting actors being both
; masters and slaves at the same time.
;
; No defined ranks yet.
;
; The special faction ::zbfFactionPlayerMaster should only be applied on the current
; owner(s) of the player character. It should always be paired with zbfFactionMaster.
;
Faction Property zbfFactionMaster Auto
Faction Property zbfFactionPlayerMaster Auto

; Used for cosmetic purposes, ie. things like adding specific dialogue to a
; particular group of people.
;
; No defined ranks yet.
;
Faction Property zbfFactionSlaver Auto

From what I understand generic faction functions can be used for working with these three factions.

Sample Code

Here is some example code for registering the player as a slave with the ZaZ Animation Pack mod:

Actor _aPlayer
zbfSlaveControl _qZbfSlave

Event OnPlayerLoadGame()
   _aPlayer = Game.GetPlayer()
   _qZbfSlave = (Quest.GetQuest("zadQuest") As zbfSlaveControl)
EndEvent

Function EnslavePlayer(Actor aNewMaster)
   If (_qZbfSlave)
      ; Register the player enslavement with the ZaZ Animation Pack.
      aNewMaster.AddToFaction(_qZbfSlave.zbfFactionMaster)
      aNewMaster.AddToFaction(_qZbfSlave.zbfFactionPlayerMaster)
      _qZbfSlave.EnslaveActor(_aPlayer, "DFW")
   EndIf
EndFunction

Function FreePlayer(Actor aMaster, Bool bEscaped)
   If (_qZbfSlave)
      ; Clear the Master with the ZaZ Animation Pack.
      aMaster.RemoveFromFaction(_qZbfSlave.zbfFactionMaster)
      aMaster.RemoveFromFaction(_qZbfSlave.zbfFactionPlayerMaster)
      _qZbfSlave.ReleaseSlave(_aPlayer, "DFW", bEscaped)
   EndIf
EndFunction

Events from the ZaZ Animation Pack

The ZaZ Animation Pack mod also sends out events when an actor is enslaved, or released:

RegisterForModEvent("zbfSC_EnslaveActor", "OnEnslaveActor")
RegisterForModEvent("zbfSC_FreeSlave", "OnFreeSlave")
RegisterForModEvent("zbfSC_ReleaseSlave", "OnReleaseSlave")
; Not yet fully ready to test, don't register these yet.
;RegisterForModEvent("zbfSC_BorrowSlave", "OnBorrowSlave")
;RegisterForModEvent("zbfSC_ReturnSlave", "OnReturnSlave")
;RegisterForModEvent("zbfSC_PauseSlave", "OnPauseSlave")
;RegisterForModEvent("zbfSC_ResumeSlave", "OnResumeSlave")

All of these use a Form (actor) and a string (mod) as parameters. Here is an example of working with these events:

Actor _aPlayer

Event OnPlayerLoadGame()
   _aPlayer = Game.GetPlayer()

   RegisterForModEvent("zbfSC_EnslaveActor", "ActorEnslaved")
   RegisterForModEvent("zbfSC_FreeSlave", "ActorFreed")
   RegisterForModEvent("zbfSC_ReleaseSlave", "ActorFreed")
EndEvent

Event ActorEnslaved(Form oActor, String szMod)
   Actor aActor = (oActor As Actor)
   If ((_aPlayer == aActor) && ("DFW" == szMod))
      ...
   EndIf
EndEvent

Event ActorFreed(Form oActor, String szMod)
   Actor aActor = (oActor As Actor)
   If ((_aPlayer == aActor) && ("DFW" == szMod))
      ...
   EndIf
EndEvent

ZaZ Animation Pack - Animations

Trying out the leash game feature of the DFW Support mod you'll notice an animation where the slaver approaches the player, bends a little, and is clearly doing something to the player. Trying out the BDSM furniture features of the DFW Support mod you'll notice an animation where various NPCs approach the player in BDSM furniture, bend a little, and appear to do something to the player or the furniture. These are both animations for the ZaZ Animation Pack and I expect some people may be interested in how to perform these animations.
Note that these two animations are not interchangeable. When in furniture the furniture animation must be used. When not in furniture the regular animation must be used. I suggest making a generic function that detects whether the player is in furniture and the starts the appropriate animation.
Here is the code that I use:

String S_MOD = "DFWS"
Function OnLoadGame()
   _qFramework = (Quest.GetQuest("_dfwDeviousFramework") As dfwDeviousFramework)
   _qZbfSlaveActions = zbfSlaveActions.GetApi()
EndFunction

Function PlayApproachAnimation(Actor aNpc, String szMessage)
   ; If we can't lock the DFW scene flag don't try to start a scene.
   If (_qFramework.SceneStarting(S_MOD, 60))
      Return
   EndIf

   ; Wait for the NPC if he is in the process of sitting or standing.
   ActorSitStateWait(aNpc)

   _qFramework.YankLeashWait(500)
   If (_qFramework.GetBdsmFurniture())
      ImmobilizePlayer()
      _qZbfSlaveActions.RestrainInDevice(None, aNpc, \
                                         asMessage=S_MOD + "_Furniture" + szMessage)
   Else
      _qZbfSlaveActions.BindPlayer(akMaster=aNpc, asMessage=S_MOD + "_" + szMessage)
   EndIf
EndFunction

You'll notice a few things about this code:

  • There is an ActorSitStateWait(aNpc) function called. I don't know whether this is necessary or not. This is to avoid starting a scene while the actor is transitioning between sitting and standing. Some things, such as activating the NPC do not work at this time so I am trying to avoid starting scenes during this time as well. You can find the code for this function below.
  • There is a call to the DFW SceneStarting() function. This is an attempt to co-ordinate scenes between mods. If another mod is in a scene this call will fail and you shouldn't start the scene. If you are not using DFW simply omit this code. Remember that you need to end the scene once it is complete as well.
  • These functions don't actually do anything. They simply play the animation. That is what szMessage is for. When the animation finishes playing you will receive an event you need to be registered for. That is where I have chosen do perform the actual actions that are being performed:
Function OnLoadGame()
   RegisterForModEvent("ZapSlaveActionDone", "OnSlaveActionDone")
EndFunction

Event OnSlaveActionDone(String szType, String szMessage, Form oMaster, \
                        Int iSceneIndex)
   ; We are only interested in animations that we started.
   If (S_MOD != Substring(szMessage, 0, 4))
      Return
   EndIf

   If (S_MOD + "_Assault" == szMessage)
      ... (Un)Restrain the player ...
      _qFramework.SceneDone(S_MOD)
   ElseIf (S_MOD + "_FurnitureAssault" == szMessage)
      ... (Un)Restrain the player while she is in furniture ...

      ; The animation unlocks the player from the furniture.
      ; Make sure she is locked back up properly.
      _qZbfSlaveActions.RestrainInDevice(_oBdsmFurniture, aMaster, S_MOD)
   ElseIf (S_MOD == szMessage)
      ; The player is now locked back up.  End the scene.
      If (0 < _iMovementSafety)
         _iMovementSafety = 0
         ReMobilizePlayer()
      EndIf
      _qFramework.SceneDone(S_MOD)
   EndIf
EndEvent

Important note: In the code above you will see a call to ImmobilizePlayer() when doing a furniture scene. This is because doing a furniture event scene unlocks the player and locks her back into the furniture. Having the player run around in the brief moment while she is unlocked causes problems. This is not the right way to play this furniture animation. I know there is a way to just play the animation without affecting the player at all; however, I have not figured out how to do that yet. Code for the ImmobilizePlayer() function is as follows:

Function ImmobilizePlayer()
   If (0 >= _iMovementSafety)
      _iMovementSafety = 20
      _fMovementAmount = (_aPlayer.GetActorValue("SpeedMult") * 0.90)
      _aPlayer.ModActorValue("SpeedMult", 0.0 - _fMovementAmount)
      _aPlayer.ModActorValue("CarryWeight", 0.1)
   EndIf
EndFunction

Movement safety is a security measure that counts down every update poll and eventually releases the player's controls in the event that the scene doesn't finish properly. Once the player's speed is changed a small change to her carry weight must be called for it to take effect. The carry weight is restored when the movement is restored.
Code for the ActorSitStateWait() function:

Function ActorSitStateWait(Actor aActor, Int iMaxWaitMs=1500)
   Int iSitState = aActor.GetSitState()
   While ((0 < iMaxWaitMs) && ((2 == iSitState) || (4 == iSitState)))
      Utility.Wait(0.1)
      iMaxWaitMs -= 100
      iSitState = aActor.GetSitState()
   EndWhile
EndFunction

Devious Devices

Eventually I would like to support both ZaZ Animation Pack devices and Devious Devices; however, since devices are not the main focus of the mod I had to start with one and focus more of my time on other aspects of the mod. For now I have been focusing on working with a limited set of Devious Devices, primarily because I could not figure out how to lock ZaZ Animation Pack devices.
Even though I am not using ZaZ Animation Pack devices I am using the ZaZ Animation Pack animations to apply Devious Devices. See the section above for more details.
Devious Devices consist of two pieces of armour, an inventory device and a rendered device.
To work with Devious Devices you need: the device type keyword, the inventory device, and the rendered device.
The keyword can be used from the zadLibs script of the zadQuest quest:

_qZadLibs = (Quest.GetQuest("zadQuest") As Zadlibs)

Normally you will have access to the inventory device and you will need to get the rendered device from that:

Armor oRestraintRendered = _qZadLibs.GetRenderedDevice(oRestraintInventory)

There are two ways to get the inventory device:

  1. From the player's inventory:
Armor oGagInventory = _qZadLibs.GetWornDevice(_aPlayer, _qZadLibs.zad_DeviousGag)
Armor oArmInventory = _qZadLibs.GetWornDevice(_aPlayer, \
                                              _qZadLibs.zad_DeviousArmbinder)
If (!oArmInventory)
   oArmInventory = _qZadLibs.GetWornDevice(_aPlayer, _qZadLibs.zad_DeviousYoke)
EndIf
If (!oArmInventory)
   oArmInventory = _qZadLibs.GetWornDevice(_aPlayer, _qZadLibs.zad_DeviousGloves)
EndIf

You can see that for some devices you won't know the exact keyword restraining the player so you may have to try several just to be sure.
If the player is not wearing inventory devices and does not have any in her inventory, you will generally want to get some randomly from a list. As far as I am aware there is no API for doing so and it is not entirely trivial. There are several mods out there that do that and it might be worthwhile to look into their code to see how it is done. As this is not the focus of my mod I have limited my code to only a few restraints and I created the list myself.
For example my pony boots list consists of:

_aoLegRestraints = New Armor[2]
;;; zadx_XinWTLPonyBootsInventory
_aoLegRestraints[00] = \
   (Game.GetFormFromFile(0x000116FA, "Devious Devices - Expansion.esm") as Armor)
;;; zadx_XinWTEbonitePonyBootsInventory
_aoLegRestraints[01] = \
   (Game.GetFormFromFile(0x000116FE, "Devious Devices - Expansion.esm") as Armor)

To get the 0x000... ID for these items I opened the Devious Devices - Expansion.esm in the creation kit under Item => Armor. I filtered the armour using "zad*Inventory" and searched the list for any footwear I found appropriate. The form ID for these items is what you would use, stripping off the first three numbers. So zadx_XinWTLPonyBootsInventory (which is 070116FA) becomes 0x000116FA.
Once you have the inventory device, the rendered device, and the keyword it is quite straightforward to add the devices to the player and remove them:

_qZadLibs.EquipDevice(_aPlayer, oInventoryCollar, oRenderedCollar, \
                      _qZadLibs.zad_DeviousCollar)
_qZadLibs.RemoveDevice(_aPlayer, oInventoryGag, oRenderedGag, \
                       _qZadLibs.zad_DeviousGag)

Zaz Animation Pack Devices

There is still much I don't know about Zaz Animation Pack (zbf) devices but here are a few things I have learned.

  1. Wrist animations are controlled via the "Offset" animation. At least this seems to be true of NPCs. At least this seems to be true when worn restraints have the keyword zbfEffectKeepOffsetAnim.
  2. To force an NPC into a pose with her hands locked behind her, it is not always enough just to wear the restraints. Make sure the offset pose is also set using:
zbfSlot qZbfActorSlot = zbfBondageShell.GetApi().SlotActor(aActor)
If (qZbfActorSlot)
   qZbfActorSlot.SetOffsetAnim("ZapWriOffset01", True)
EndIf
  1. The different possible offset (wrist) animations are: {"ZapWriOffset01", "ZapYokeOffset01", "ZapArmbOffset01"}.

Reporting Quest Variables

Sometimes it is necessary to get script and quest variables to mod authors. This can be a bit of an involved process but it helps diagnose problems greatly. There are four steps to getting this done:

  1. Make sure Papyrus Logging is turned on. This is described in a number of places. An Internet search should provide you with instructions. Make sure your Skyrim.ini files has the following section:
[Papyrus]
fPostLoadUpdateTimeMS=500.0
bEnableLogging=1
bEnableTrace=1
bLoadDebugInformation=1

You Skyrim.ini file should be located in your Windows user's Documents directory under "My Games"\Skyrim or somewhere similar.

  1. Make sure commands from your console also get printed in your papyrus log file. To achieve this you need to install a mod called ConSkrybe from the Nexus mod repository:
    https://www.nexusmods.com/skyrim/mods/19630

  2. Run your game and print the quest variables to your console. With ConSkrybe running this should automatically dump the variables into the ConSkrybe log file. Open the console using the tilde key (` beside the 1 on most keyboards) then enter the following two commands:

ShowQuestVars _dfwDeviousFramework
ShowQuestVars _dfwsDfwSupport

Now is a good time to perform the actions that are not working so they can be reported to the log file as well.
4. Quit the game and provide two files to the mod author:
The ConSkrybe log file containing a list of all variables in the scripts. "ConSkrybe.log" located in the Skyrim games folder "Steam\steamapps\common\Skyrim"
Your papyrus log file containing log files from the most recent game where you performed the actions that are not working. The papyrus log file should be located in your Windows user's Documents directory under "My Games"\Skyrim\Logs\Script or somewhere similar. The log file for the last game played would be "Papyrus.0.log".

Note: Another good way to get quest variables to the mod developer is to send them your saved game files; although, save game files are more difficult to work with. For example, you can get before and after variables in a single ConSkrybe log file, ConSkrybe log files are text based making them viewable by everyone, and copy/paste/comparison of a ConSkrybe log file is significantly easier than save game files.

Making a Clean Save

If you don't want all of the background information you can skip down to the How to Clean section.

A Word of Warning

The makers of the Unofficial Skyrim patch advise against ever cleaning a save game file and I very much expect those developers know what they are talking about. Unfortunately, I don't know their rationale behind this comment and I know that in some extreme circumstances my mod won't function correctly without a clean save.
At this time I must continue to recommend a clean save for some upgrades (version 2.00 for example) and I don't know of a way around it.
A quote from Skyrim Unofficial Patch FAQ
"Do I need to do a clean save when upgrading?"
"No! When upgrading one of the unofficial patches, you simply need to copy the new files in and overwrite the old ones. NEVER follow advice given by someone to do a "clean save". In Skyrim, there is no such thing. Performing one can lead to corrupting your save, and nobody really wants that."

Introduction

A lot of places on the internet talk about "Clean Saves" but it is rarely explained what a clean save is.
A save game file contains many things. Of particular note are the scripts used by the mods. In each save game there is a copy of each mod's "scripts" as well as all of the "script instances" actively in use.
I use something called "Save Game Script Cleaner" (a.k.a. "SaveTool") to clean (and to look at) save game files:
http://www.nexusmods.com/skyrim/mods/52363
Another tool in the community is Savegame script scalpel. It looks like these might both be replaced by a newer tool called ReSaver of SkyrimTools.

A Note About Scripts (for modders)

Looking at your save game files you will see the "Scripts" and "ScriptInstances" section of the saved game. Unless I am mistaken, each script is separated into parts common to all instance of the script (i.e. "Scripts") and parts that need to be different for each instance of the script.
The common part of each script is primarily the code, the behaviour of the script. This is updated from the script's file on disk regularly. Most likely when each game is loaded.
The instance specific part of each script is primarily variables for each instance of the script. Variables can change over time and each instance of the script needs to keep track of the values of its variables separately. An important thing to understand is these values are never updated. If new variables are added to a script, all script instances will pick up the new variable; however, (I believe) these variables will be initialized to 0, regardless of what their initial value in the script is. Additionally, once these variables have a value in the script they are set until running code changes them. Changing their initial values in the script will not affect values in instances of scripts that have already been created.

Reasons to Clean

When a mod is uninstalled none of these scripts will be removed from the saved games. Additionally, if a new version of the mod is installed all of the old versions of the script will still exist and in many cases the game will try to use the old script instances saved in the game. While the game can handle the cases of mods disappearing as well as scripts and variables that don't exist by ignoring them, sometimes having the game attempt to use old information from a saved game can crash the game regularly, making the saved game file more or less unplayable.
Cleaning your save game files regularly removes old information from the file, greatly improving the stability of the saved game file.
Additionally, if a mod author has suggested that an upgrade requires a clean save, it is very likely that they have made a change to the scripts which they know won't be compatible with old information in a saved game file. In this case the game may not crash, but the mod won't quite work as expected if your saved games aren't cleaned.
In addition to scripts there are timers and a number of other things in the saved game file that can get out of date.
Although game can be cleaned with the mod still installed, it is significantly easier to clean a mod that has been uninstalled. The saved game cleaning tools have the ability to look for scripts and information for mods that are no longer in use and delete them all. It is recommended to uninstall mods that need to be cleaned.

How to Clean

  1. Download the Save Game Script Cleaner.
  2. Save your game under a new save. Ideally this should be in a small indoor area with no one nearby and the area should not contain any features of the mod being uninstalled.
  3. [Optional]: For Devious Framework upgrade the mod to at least version 2.06 (or the latest version if possible). Wait for initialization to complete. This step may only be needed if you are encountering problems without it.
  4. For Devious Framework and DFW Support select "Shutdown Mod" from the MCM and wait (for maybe 10 - 60 seconds. No message will appear). Then save the game again under a new save.
  5. Disable the the mod(s) to be cleaned.
    Do not uninstall the mod. At least for Devious Framework and DFW Support I found the .pex files need to exist in the scripts directory in order for the game to be loaded, even if no DFW scripts appear to exist in the saved game after it has been cleaned.
  6. Load the game and save it again under a new save. I believe this marks missing scripts as "orphaned" in the save game file.
    In addition to loading the game and saving the game again it may help to wait for some time in game before saving. This will clean up some of the timers that should no longer exist. (2 - 24 hours or 10 - 60 seconds real time maybe?)
  7. Load your saved game in the Save Game Script Cleaner.
  8. Remove all references to the mod(s).
    For the DFW mod the recommended method of doing this is using the "FixScriptInstances" and "Delete all #" features of the Save Game Script Cleaner.
    You can see all of the DFW scripts by typing "dfw" in the filter box in the top left corner of the Scripts section. Try deleting any leftover dfw scripts using "Delete Selected" as well.
  9. Open Skyrim and load the game saved from the Save Game Script Cleaner. Wait 10 - 60 seconds and save the game again under a new save.
  10. Exit, uninstall the mod, reinstall the mod, and load the saved game.
  11. Remember that for DFW the game needs to be saved and loaded once the scripts are initialized for some features to work properly.
⚠️ **GitHub.com Fallback** ⚠️