Hex Bolt Posted September 13 Posted September 13 (edited) 39 minutes ago, HereBeTentacles said: It still works that way, that's why it would need to be done through some sort of magic effect. Anything you do to the player speedmult directly is likely to get reset at some point. If you do it through a magic effect or some other indirect method it should be fine. Plus if you do it that way you can condition the effect to only apply when the player is wearing something with the petssuit keyword. Less scripting that way depending on what you are doing. I have a similar sort of thing in my mod where I overencumber the player because I don't want them running/sprinting, but that leaves them too slow for my liking so I add an ability that ups their speedmult a bit to make up for that. The same idea could work for your mod. Well, to back up a bit, I already handled the pet suit's speed debuff for DD 5.2 by temporarily changing the value for the hobble dress speed debuff (which is used for the pet suit enchantment's speed debuff). That value came the MCM and it's stored as a script property, easy to access. That works great, because DD's own logic maintains the desired speed so that it's not too slow. Fire & forget, DD handles it. However, DD NG removed that logic from zadx_HobbleSkirtEffectScript. The speed value seems to come from afMaxSpeedMult in DeviousDevices.ini, so my assumption (always risky, those) is that the periodic updates moved into the dll. That's an unfortunate loss of accessibility, because the ini setting is not exposed to a script. Now, I'm confused. I might have misunderstood, but I thought you had suggested adding a speedmult modifier to counter the pet suit's debuff, but you also told me that DD NG still periodically adjusts the speed of the pet suit's wearer. That's what DD 5.2 does; every 5 seconds, it compares the current speedmult actor value to a target speed and adjusts the debuff to make the current speed comply. So, if it still works the same way with periodic updates, my adding an increase to speed through a magic effect will just get countered by DD NG a few seconds later. I definitely don't want to add a periodic update of my own to fight with DD NG's. That is why I had asked how to access DD NG's speed debug value from a script, so I can temporarily adjust it for my event and then restore the original value afterwards. So, what am I missing? Edited September 13 by Hex Bolt
HereBeTentacles Posted September 14 Posted September 14 (edited) 50 minutes ago, Hex Bolt said: Now, I'm confused. I might have misunderstood, but I thought you had suggested adding a speedmult modifier to counter the pet suit's debuff Little misunderstanding, this is not what I am suggesting or what would happen, think of it more like ad DD is adjusting the players base speedmult via script and then the speedmult magic effect I am suggesting is just getting added to it at a later step. Like how right now if I set my charcters health from 130 to 100 while wearing a +30 health ring, it will still say it was 130 after the change because the ring +30 is separate from my characters health. It would the same way for you except instead of a ring its a magic effect and instead of health its speedmult. Just to make sure this works properly I just tried it in my game. I made it so if I press middle mouse my character gets a 400 speedmult from a an ability I set up. I put on a pet suit, press middle mouse, and off I go. You could do the same except instead of needing to be pressing middle mouse the ability just checks if you are wearing a petsuit. The perk does need to periodically check if you are wearing a petsuit or not, but theres no scripting there, its all done through the engine which is a non-issue for a simple keyword check. The only scripting you would need for that is a script to add the ability to the player. This is just what I do though, could be other ways. Only issue I have seen with this method is the engine only seems to poll conditions twice a second so it can only update the speedmult that fast. Not too big of an issue though. Edited September 14 by HereBeTentacles 1
Hex Bolt Posted September 14 Posted September 14 (edited) 1 hour ago, HereBeTentacles said: DD is adjusting the players base speedmult via script and then the speedmult magic effect I am suggesting is just getting added overtop of it. Like how right now if I set my charcters health from 130 to 100 while wearing a +30 health ring, it will still say it was 130 after the change because the ring +30 is separate from my characters health. Okay. Since the logic for that moved out of zadx_HobbleSkirtEffectScript in DD NG and I couldn't find it elsewhere, I couldn't be sure what DD NG is doing with that. DD 5.2 constantly corrected speed to match its own target (add a speed buff and DD would cancel it with its own debuff seconds later). From you've told me, it seems that DD NG doesn't keep correcting for speed buffs, so I can modify the wearer's speed once after the pet suit is equipped and have the wearer move faster, then remove the speed adjustment at the end of the event when the suit comes off. Thank for the explanation and for your test. Edited September 14 by Hex Bolt
HereBeTentacles Posted September 14 Posted September 14 15 minutes ago, Hex Bolt said: Okay. Since the logic for that moved out of zadx_HobbleSkirtEffectScript in DD NG and I couldn't find it elsewhere, I couldn't be sure what DD NG is doing with that. DD 5.2 constantly corrected speed to match its own target (add a speed buff and DD would cancel it with its own debuff seconds later). From you've told me, it seems that DD NG doesn't keep correcting for speed buffs, so I can modify the wearer's speed once after the pet suit is equipped and have the wearer move faster, then remove the speed adjustment at the end of the event when the suit comes off. That's easy. Thank for the explanation and for your test. From what I tried 300 seemed to be a little under normal walk/run/sprint speed. The petsuit animations weren't really made for higher speeds so I think if you go past 450ish the sprint animation is going to look a bit off.
Hex Bolt Posted September 14 Posted September 14 (edited) 1 hour ago, HereBeTentacles said: From what I tried 300 seemed to be a little under normal walk/run/sprint speed. The petsuit animations weren't really made for higher speeds so I think if you go past 450ish the sprint animation is going to look a bit off. Hold on, now I'm confused again. NG's handling of this is really not transparent. Why do you need to apply a speedmult of 300 to 400 to reach average speed? That's extreme, right? Speedmults are additive. If DD NG adjusts the player's speed to 15 (afMaxSpeedMult = 0.15 in the ini file), adding 85 is all that's needed to get to 100, normal speed. Now, if DD NG is instead multiplying the multiplier, things get messy. My mod doesn't know what multiplier DD NG is using because that moved from being a script property to an SKSE INI setting. As far as I can tell, DD NG does not expose the afMaxSpeedMult value, so mods can't see it. All I can see is the player's current speedmult actor value, which got multiplied by a hidden factor to its current value. Players are free to change DD NG's afMaxSpeedMult, but my mod can't see that, so it won't know how much to increase the player's speedmult if DD NG is going to multiply that addition. I could use 300 and hope for the best, but I've had some players tell me that they've increased afMaxSpeedMult a lot because they dislike being so slow. Players who have done that will move crazy fast if I buff their speedmult by 300 or 400. I do appreciate all the time you've taken to respond. Maybe I'm dense, but how this works just isn't clear to me. I'd like to understand how I can work with the system to achieve a specific desired speed. Edited September 14 by Hex Bolt 1
HereBeTentacles Posted September 14 Posted September 14 12 minutes ago, Hex Bolt said: Hold on, now I'm confused again. Compared to 5.2, NG's handling of this is really not transparent. Why do you need to apply a speedmult of 300 to 400 to reach average speed? That's extreme, right? Speedmults are additive. If DD NG adjusts the player's speed to 15 (afMaxSpeedMult = 0.15 in the ini file), adding 85 is all that's needed to get to 100, normal speed. Now, if DD NG is instead multiplying the multiplier, things get messy. My mod doesn't know what multiplier DD NG is using because that moved from being a script property to an SKSE INI setting. As far as I can tell, DD NG does not expose the afMaxSpeedMult value, so mods can't see it. All I can see is the player's current speedmult actor value, which got multiplied by a hidden factor to its current value. Players are free to change DD NG's afMaxSpeedMult, but my mod can't see that, so my mod won't know how much to increase the player's speedmult if DD NG is going to multiply that addition. I could use 300 and hope for the best, but I've had some players tell me that they've increased afMaxSpeedMult a lot because they dislike being so slow. Players who have done that will move crazy fast if I buff their speedmult by 300 or 400. I would just leave it up to a button in an MCM or something like that so they can toggle on/off in case they already adjusted the speed elsewhere. I don't think it would be impossible to expose the modifiers DDNG is using to a script for more fine control though, pretty sure the SKSE should be able to do that.
Hex Bolt Posted September 14 Posted September 14 Just now, HereBeTentacles said: I would just leave it up to a button in an MCM or something like that so they can toggle on/off in case they already adjusted the speed elsewhere. I had considered that, but it's kind of clunky for another mod to have to add an adjustment toggle when it might have simply calculated the precise adjustment needed and applied that without player intervention. It seems as if there should be a straightforward way to do that. Prior to the zadx_HobbleSkirtEffectScript change, it wasn't hard. I'm just trying to figure out how to achieve the same result with the change. Again, thank you for the responses.
CaptainJ03 Posted September 14 Posted September 14 4 hours ago, Hex Bolt said: Players are free to change DD NG's afMaxSpeedMult, but my mod can't see that, so it won't know how much to increase the player's speedmult if DD NG is going to multiply that addition. I could use 300 and hope for the best, but I've had some players tell me that they've increased afMaxSpeedMult a lot because they dislike being so slow. Players who have done that will move crazy fast if I buff their speedmult by 300 or 400. Yes, that would be me... The very odd thing is: Sometimes your approach works. Mistress puts Lola in a petsuit and the speed is much slower than my usual (which I changed in DD.ini) and when it's changed to let me move faster, it's back to normal-ish (can't tell exactly because there's no speedometer on my petsuit) But as we are talking hobbleskirts: There's still a bug which has been present since... some years. The last step is always a stagger. With the last NG version it's smooth when the dress is freshly put on, but loading a save with the hobbleskirt worn, the stagger is back.
Hex Bolt Posted September 14 Posted September 14 50 minutes ago, CaptainJ03 said: there's no speedometer on my petsuit Player.getav speedmult 1
Lady Wintrish Posted September 14 Posted September 14 (edited) Some of the heels are going under the ground with heels fix and DD NG 0.4 Edited September 14 by Lady Wintrish
CliftonJD Posted September 14 Posted September 14 (edited) On 9/10/2026 at 10:41 PM, DonQuiWho said: FYI - in case it's not been mentioned before Using this version with these settings: Devious Devices NG v0.4.3 + FeuerTin + Small Steps + Hop is Sprint + FH + No Dyn Armour Straitjacket ; HobbleDress: Red Ebonite doesn't hide hands whhich seem to be posed as in an armbinder Pics In situ and + Console below Hope that's of use DQW Reveal hidden contents at this ive found that dd straitjackets dont mark the hand slot as used so slaves will often either show the hands or additionally equip gloves. pahe includes fixes for that, but agreed it would be better handled from dd On 9/10/2026 at 10:09 AM, DonQuiWho said: @TrollAutokill 's Diary of Mine (DOM), his NPC slavery mod developed from Paradise Halls Extended (PAHE) makes for excellent gameplay with respect to the more emotional aspects of NPC training However, as it has developed further from the original PAHE, its handling of NPC restraint/s has changed so that, to a large extent, it no longer allows the player to easily directly control worn restraints, either ZAZ or DD/DDNG based. Consequently it has lost some of its 'playability' for those of us who, lacking relational finesse, would really want to use it more as a 'Trading Entity' of the sort of 'Catch, Train and Sell' variety. Sort of like when it first appeared on NEXUS a dozen years or so ago. (The joys of being🧓) The post linked to at the end sets out the sort thing that can happen in DOM presently - eg restraints vanishing as NPCs poses etc change - and which right now seem to be beyond the player's ability to properly control. It's NOT a reflection on poor mod development - far from that! - but rather the author's greater interest in the emotive aspects, and as TAK has said, he doesn't use DDs, and I guess that his use of ZAZ items is pretty much picking up on the PAHE legacy. He seems happy about being willing to try to help on this - see the post I referred to - but basically it seems that any primary mechanism to do that might require some identification of ZAZ & DD items by keywords so that they can be identified as some sort of 'no strip' inventory items, and as you can see from the post attached, also able would need to ensure that for DD items BOTH the 'worn' Equipment and 'unworn' Inventory items are left in situ - at present once can disappear 😊 I have had a good search through some of the varied items my player has commanded NPCs to wear - from manacles, armbinders and the full set of chains - but I can't see any readily common way of identifying them directly from the DD individual device keywords. The only thing that I could see that was common overall was that the keywords' ultimately link back to the base DD mods., but I don't see how even looking at load orders could actually do that too easily when 2 lead characters are variable from set-up to set-up BUT, a lot of mods out there can detect worn DDs so that they don't strip them, or at least quest DDs, from players or NPCs inadvertently eg Simple Slavery etc So is there some magic that can be done in this regard? I am the ultimate non techie. What would allow TAK to add something to devices applied to DOM slaves that he could then use as a means to check items worn by DOM NPC which would prevent his other functions - eg pose animation changes etc - from inadvertently removing them? I have had a look through the thread for 'keyword' uses, in case there really was some simple catch all, but there wasn't anything that I could identify - or understand - as being particularly useful. But what would I know ...😜 The real gurus on this eg @krzp are here, so any suggestions for something simple that would help in this would be gratefully received! DQW On 9/11/2026 at 7:13 PM, DonQuiWho said: Thanks very much for the prompt, and succinct, reply! I'll pass that on, and see if that helps with this conundrum! DQW for that hes welcome to look back how pahe handles it. pahe already checks for the devious keywords and works accordingly Edited September 14 by CliftonJD
DonQuiWho Posted September 14 Posted September 14 10 minutes ago, CliftonJD said: at this ive found that dd straitjackets dont mark the hand slot as used so slaves will often either show the hands or additionally equip gloves. pahe includes fixes for that, but agreed it would be better handled from dd for that hes welcome to look back how pahe handles it. pahe already checks for the devious keywords and works accordingly Thanks very much I'm sure @TrollAutokill will be watching this thread and have noted those helpful remarks Much appreciated DQW 2
sacattack93 Posted September 15 Posted September 15 (edited) Crash whenever I try to start a new game or load a game with DDNG. Build works perfectly fine without DD/DDNG installed. Anybody know of a fix? Crash log: https://pastebin.com/Grr66MFs Load order: https://pastebin.com/uzpivUrQ thx!! Edited September 15 by sacattack93
billypark2192 Posted September 15 Posted September 15 I remember using a DD mod in the past where, if the player was wearing a gag, trying to use a Shout would not actually perform the attack. Instead, the character would make muffled moaning sounds. Does anyone know which DD mod or feature does this?
WWWGONA Posted September 16 Posted September 16 On 8/21/2026 at 9:07 PM, bondjamesbond said: Does anyone else have an issue with device manipulation not working? When removing the device, the message will say the device has been removed, but then the device will re-equip. I've had this issue for the last few versions of DD NG now, but cannot find anything overwriting the mod. Devices will remove if an animation plays out and the attempt was sucessful, but that can take numerous times, even on the easiest difficulty. I have same problem. I identified the problem with the rope restraints and the plug. By any chance, have you solved this problem? The only DD-related mod I use is Devious Curse.
WWWGONA Posted September 16 Posted September 16 2 hours ago, WWWGONA said: I have same problem. I identified the problem with the rope restraints and the plug. By any chance, have you solved this problem? The only DD-related mod I use is Devious Curse. Analysis of the source code and Papyrus logs using ChatGPT suggests that the re-equipping bug likely stems from the fact that, in the unconditional removal path, `OnUnequipped()` triggers before the `RemovalToken` is set—a token DD NG uses to distinguish between a user's intentional removal and an unequip event triggered internally by the mod.
Justinof Posted September 18 Posted September 18 On 9/15/2026 at 2:39 AM, sacattack93 said: Crash whenever I try to start a new game or load a game with DDNG. Build works perfectly fine without DD/DDNG installed. Anybody know of a fix? Crash log: https://pastebin.com/Grr66MFs Load order: https://pastebin.com/uzpivUrQ thx!! Your DD/DDng installation is not correct: you should have 4 .esm in your load order (Assets, Integration, Expansion, Contraptions) while there is only the first. Solution: reinstall both mods. 1
bondjamesbond Posted September 18 Posted September 18 On 9/15/2026 at 10:19 PM, WWWGONA said: I have same problem. I identified the problem with the rope restraints and the plug. By any chance, have you solved this problem? The only DD-related mod I use is Devious Curse. Thank you for the reply. I have not solved the problem on my end yet. I have never had the mod Devious Curse in my load order, so you can hopefully rule that out. I've tried removing a few mods with possible interactions related to DD, but haven't had luck there. I don't see any conflicts with DD NG in Vortex, as DD NG wins all. I tried the DD log that someone mentioned, but it is difficult to interpret those. My log did mention device removal token like you described, but I didn't see anything in the log pointing to a mod that I had which could be causing any issue. I wonder if something is happening too quick without the struggle animation. I didn't mention this in my original post, but the device manipulation function isn't the only way I've had the device re-equip issue. This issue also happens if I sit the player in a chair and then do the struggle/pick lock/break device, and have a successful outcome. Sitting in the chair before the attempt basically just bypasses the animation,. I did this in the past when I didn't want to sit through repeated 10-20 second animations. If I ever figure out the issue on my end, I will post what I did to fix it. Hopefully very few people have run into this.
skofild24 Posted September 19 Posted September 19 On 9/12/2026 at 11:58 PM, HereBeTentacles said: Most likely you are trying to use DDNG on the latest version of skryim, which you cannot do yet. You will either have to wait or roll your game back to one of the previous supported versions. The funniest thing is that with version DD NG 0.4.1.7, the game actually launched after the latest Bethesda update—it just complained: "must recompiled for new address library."
WWWGONA Posted September 19 Posted September 19 18 hours ago, bondjamesbond said: Thank you for the reply. I have not solved the problem on my end yet. I have never had the mod Devious Curse in my load order, so you can hopefully rule that out. I've tried removing a few mods with possible interactions related to DD, but haven't had luck there. I don't see any conflicts with DD NG in Vortex, as DD NG wins all. I tried the DD log that someone mentioned, but it is difficult to interpret those. My log did mention device removal token like you described, but I didn't see anything in the log pointing to a mod that I had which could be causing any issue. I wonder if something is happening too quick without the struggle animation. I didn't mention this in my original post, but the device manipulation function isn't the only way I've had the device re-equip issue. This issue also happens if I sit the player in a chair and then do the struggle/pick lock/break device, and have a successful outcome. Sitting in the chair before the attempt basically just bypasses the animation,. I did this in the past when I didn't want to sit through repeated 10-20 second animations. If I ever figure out the issue on my end, I will post what I did to fix it. Hopefully very few people have run into this. I have successfully reproduced the exact issue you are experiencing. Based on my findings, the re-equipping bug occurs in the following scenarios: 1. Equipment that can be removed without conditions, such as plugs or rope cuffs. 2. Equipment configured via MCM settings to allow removal without conditions. 3. Equipment removed while the character is in a crouching position. The common factor among these three scenarios is that the equipment removal animation does not play. After analyzing all DDNG .psc script files and bug logs using GPT, the following report has been generated. "The temporary OnEquipped() instance created during device-menu handling survives long enough to observe the completion of an actual removal, but has no mechanism to distinguish that completed removal from a normal device swap. It subsequently executes its normal equipment logic and re-equips the device. In other words, the fundamental bug is a state/race-condition failure in the synchronization between the temporary re-equip operation and the actual removal operation, with the five-second zad_RemovalOperation wait acting as the critical synchronization boundary."
WWWGONA Posted September 19 Posted September 19 On 8/21/2026 at 9:07 PM, bondjamesbond said: Does anyone else have an issue with device manipulation not working? When removing the device, the message will say the device has been removed, but then the device will re-equip. I've had this issue for the last few versions of DD NG now, but cannot find anything overwriting the mod. Devices will remove if an animation plays out and the attempt was sucessful, but that can take numerous times, even on the easiest difficulty. I make bug fix with ChatGPT. Test Please. DDNG Re-Equip Bug Fix Patch Logic Structure and Changes Compared with the Original zadEquipScript.psc 1. Scope and Purpose This patch modifies zadEquipScript.psc to address the specific re-equip problem caused by an OnEquipped() event that remains alive while a device-removal operation is in progress. The original script intentionally re-equips the inventory device from OnUnequipped() so that the player can access DeviceMenu(). That intentional EquipItem() call generates another OnEquipped() event. The problem occurs when the actual removal finishes before that OnEquipped() event has finished waiting on zad_RemovalOperation. The original event does not terminate when the removal succeeds. Once the wait ends, it can continue through the normal equipment path and execute EquipItem() again, causing the already-removed device to be re-equipped. The final patch detects this obsolete OnEquipped() invocation and aborts it before it reaches the actual equipment calls. 2. Original OnEquipped() Logic The relevant player-side logic in the original script is: OnEquipped() ↓ Check zad_RemovalOperation ↓ If active: wait up to 5 seconds ↓ Continue processing ↓ Equipment / conflict / filter checks ↓ OnEquippedPre() ↓ EquipItem(DeviceInventory) ↓ EquipItem(DeviceRendered) The important limitation is that the original code only asks whether zad_RemovalOperation has ended. It does not ask whether the device that this particular OnEquipped() invocation was waiting for has actually been removed. 3. The Original Re-Equip Mechanism OnUnequipped() deliberately contains this behavior for the player: akActor.EquipItem(deviceInventory, false, true) DeviceMenu() This is intended to let the player access the device interaction menu. The resulting event sequence can therefore be: OnUnequipped() ↓ zad_RemovalOperation = 1 ↓ Temporary EquipItem(deviceInventory) ↓ New OnEquipped() ↓ Wait while removal operation is active If the device is then genuinely removed, the normal removal branch processes the RemovalToken, calls UnsetStoredDevice(), and eventually clears zad_RemovalOperation. However, the original OnEquipped() that was waiting does not disappear. When the wait ends, it continues executing. If the rendered device is no longer present, it can eventually reach: if !akActor.IsEquipped(DeviceInventory) akActor.EquipItem(DeviceInventory, false, true) EndIf akActor.EquipItem(DeviceRendered, true, true) Those are the actual re-equipping calls responsible for the bug. 4. New Logic Added by the Final Patch The final patch keeps the original five-second wait, but records whether this particular OnEquipped() invocation entered that wait. bool waitingForRemoval = false if StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice, 0 ) == 1 waitingForRemoval = true EndIf This is a local variable belonging to the current OnEquipped() execution. No additional persistent StorageUtil wait/completion flags are introduced. 5. How the Patch Determines Whether the Device Was Actually Removed After the wait ends, the patch examines DDNG's existing stored-device state: Armor storedDevice = StorageUtil.GetFormValue( akActor, "zad_Equipped" + libs.LookupDeviceType(zad_DeviousDevice) + "_Inventory" ) as Armor It then checks: if storedDevice != deviceInventory \ && !akActor.IsEquipped(deviceRendered) \ && akActor.GetItemCount(deviceRendered) <= 0 return EndIf The three checks serve different purposes. Condition A — The stored inventory device is no longer this device The normal successful-removal branch calls UnsetStoredDevice(). That removes the stored _Inventory form for the device type. Therefore, if the current deviceInventory is no longer the stored device, the old equipment event is no longer associated with an active stored restraint of that type. Condition B — The rendered device is not currently equipped !akActor.IsEquipped(deviceRendered) This prevents the stale-event test from firing merely because the stored form changed while the device is still physically present. Condition C — The rendered device is not in the inventory akActor.GetItemCount(deviceRendered) <= 0 This further distinguishes a genuinely removed device from a device that is temporarily in a different equipment state. Only when all three conditions are satisfied does the patch conclude that this OnEquipped() invocation is obsolete. It then returns immediately. 6. Final Control Flow Normal temporary re-equip OnUnequipped() ↓ RemovalOperation = 1 ↓ Temporary EquipItem() ↓ OnEquipped() ↓ waitingForRemoval = true ↓ Wait up to 5 seconds ↓ No genuine removal occurred ↓ Stored device is still this device ↓ Continue original equipment processing The original menu-access behavior is therefore preserved. Fast removal without an animation OnUnequipped() ↓ Temporary EquipItem() ↓ OnEquipped() ↓ RemovalOperation = 1 ↓ Wait ↓ RemoveDevice() ↓ UnlockDevice() ↓ OnUnequipped() ↓ RemovalToken detected ↓ UnsetStoredDevice() ↓ RemovalOperation cleared ↓ Waiting OnEquipped resumes ↓ Stored device != current device + rendered device not equipped + rendered device absent from inventory ↓ return The obsolete OnEquipped() therefore never reaches the final EquipItem() calls. Removal while seated / animation path does not execute Removal request ↓ Animation path entered ↓ Animation path terminates without executing the animation ↓ RemoveDevice() ↓ Actual removal ↓ Waiting OnEquipped resumes ↓ Device is confirmed to be gone ↓ return This follows the same protection mechanism as other rapid-removal cases. 7. Exact Code-Level Changes Change 1 — Added a local state variable Original: int counter = 10 Final patch additionally introduces: bool waitingForRemoval = false and sets it when zad_RemovalOperation is active. Change 2 — Added an explicit default value The original uses: StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice ) The final patch uses: StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice, 0 ) This explicitly treats a missing value as zero. Change 3 — Added stale-event detection after the wait The original has no post-wait test that distinguishes a genuinely valid equipment event from an equipment event whose corresponding device has already been removed. The final version reads the existing stored inventory form and checks: storedDevice != deviceInventory && !akActor.IsEquipped(deviceRendered) && akActor.GetItemCount(deviceRendered) <= 0 When the condition is satisfied, the event is logged and immediately terminated. Change 4 — The actual OnUnequipped() removal mechanism is unchanged The final patch does not alter the normal removal sequence: akActor.RemoveItem(deviceRendered, 1, true) UnsetStoredDevice(akActor) OnRemoveDevice(akActor) ... StorageUtil.UnsetIntValue( akActor, "zad_RemovalToken" + deviceInventory ) Nor does it change the final clearing of: zad_RemovalOperation + zad_DeviousDevice in OnUnequipped(). The patch therefore does not replace the removal system itself. It only prevents an obsolete OnEquipped() invocation from continuing after that removal has already completed. 8. Improvement over the Earlier Test Patch An earlier test version used two additional persistent StorageUtil flags: zad_RemovalWait zad_RemovalCompleted The final patch removes those extra persistent states. Instead, it uses information DDNG already maintains: zad_Equipped..._Inventory actual rendered-device equipped state rendered-device inventory count This reduces the amount of patch-specific persistent state and avoids having separate wait/completion markers that could be overwritten or left behind when multiple equip/removal events overlap. 9. Features Intentionally Left Untouched The patch does not modify: quest-item removal protection OnUnequippedFilter() zad_RemovalToken zad_TightenToken zad_UntightenToken linked-device processing equipment conflict checks ShouldEquipSilently() OnEquippedPre() / OnEquippedPost() bound-effect handling the existing remove-all-items compatibility re-equip logic The patch is deliberately limited to discarding the obsolete OnEquipped() invocation created by the removal/re-equip race. 10. Core Difference The original behavior is effectively: "An OnEquipped() that was waiting for removal continues executing after that removal finishes." The final behavior is: "An OnEquipped() that was waiting for removal checks whether its device has actually disappeared. If it has, that old event is discarded." The patch therefore does not globally disable EquipItem(), does not disable UpdateControls(), and does not remove DD's intended menu-access re-equip behavior. It specifically limits the lifetime of the stale OnEquipped() event that causes the re-equipping race. Devious device NG 0.4.3 RE_Equip_Bug_fix.zip
bondjamesbond Posted September 19 Posted September 19 3 hours ago, WWWGONA said: I make bug fix with ChatGPT. Test Please. DDNG Re-Equip Bug Fix Patch Logic Structure and Changes Compared with the Original zadEquipScript.psc 1. Scope and Purpose This patch modifies zadEquipScript.psc to address the specific re-equip problem caused by an OnEquipped() event that remains alive while a device-removal operation is in progress. The original script intentionally re-equips the inventory device from OnUnequipped() so that the player can access DeviceMenu(). That intentional EquipItem() call generates another OnEquipped() event. The problem occurs when the actual removal finishes before that OnEquipped() event has finished waiting on zad_RemovalOperation. The original event does not terminate when the removal succeeds. Once the wait ends, it can continue through the normal equipment path and execute EquipItem() again, causing the already-removed device to be re-equipped. The final patch detects this obsolete OnEquipped() invocation and aborts it before it reaches the actual equipment calls. 2. Original OnEquipped() Logic The relevant player-side logic in the original script is: OnEquipped() ↓ Check zad_RemovalOperation ↓ If active: wait up to 5 seconds ↓ Continue processing ↓ Equipment / conflict / filter checks ↓ OnEquippedPre() ↓ EquipItem(DeviceInventory) ↓ EquipItem(DeviceRendered) The important limitation is that the original code only asks whether zad_RemovalOperation has ended. It does not ask whether the device that this particular OnEquipped() invocation was waiting for has actually been removed. 3. The Original Re-Equip Mechanism OnUnequipped() deliberately contains this behavior for the player: akActor.EquipItem(deviceInventory, false, true) DeviceMenu() This is intended to let the player access the device interaction menu. The resulting event sequence can therefore be: OnUnequipped() ↓ zad_RemovalOperation = 1 ↓ Temporary EquipItem(deviceInventory) ↓ New OnEquipped() ↓ Wait while removal operation is active If the device is then genuinely removed, the normal removal branch processes the RemovalToken, calls UnsetStoredDevice(), and eventually clears zad_RemovalOperation. However, the original OnEquipped() that was waiting does not disappear. When the wait ends, it continues executing. If the rendered device is no longer present, it can eventually reach: if !akActor.IsEquipped(DeviceInventory) akActor.EquipItem(DeviceInventory, false, true) EndIf akActor.EquipItem(DeviceRendered, true, true) Those are the actual re-equipping calls responsible for the bug. 4. New Logic Added by the Final Patch The final patch keeps the original five-second wait, but records whether this particular OnEquipped() invocation entered that wait. bool waitingForRemoval = false if StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice, 0 ) == 1 waitingForRemoval = true EndIf This is a local variable belonging to the current OnEquipped() execution. No additional persistent StorageUtil wait/completion flags are introduced. 5. How the Patch Determines Whether the Device Was Actually Removed After the wait ends, the patch examines DDNG's existing stored-device state: Armor storedDevice = StorageUtil.GetFormValue( akActor, "zad_Equipped" + libs.LookupDeviceType(zad_DeviousDevice) + "_Inventory" ) as Armor It then checks: if storedDevice != deviceInventory \ && !akActor.IsEquipped(deviceRendered) \ && akActor.GetItemCount(deviceRendered) <= 0 return EndIf The three checks serve different purposes. Condition A — The stored inventory device is no longer this device The normal successful-removal branch calls UnsetStoredDevice(). That removes the stored _Inventory form for the device type. Therefore, if the current deviceInventory is no longer the stored device, the old equipment event is no longer associated with an active stored restraint of that type. Condition B — The rendered device is not currently equipped !akActor.IsEquipped(deviceRendered) This prevents the stale-event test from firing merely because the stored form changed while the device is still physically present. Condition C — The rendered device is not in the inventory akActor.GetItemCount(deviceRendered) <= 0 This further distinguishes a genuinely removed device from a device that is temporarily in a different equipment state. Only when all three conditions are satisfied does the patch conclude that this OnEquipped() invocation is obsolete. It then returns immediately. 6. Final Control Flow Normal temporary re-equip OnUnequipped() ↓ RemovalOperation = 1 ↓ Temporary EquipItem() ↓ OnEquipped() ↓ waitingForRemoval = true ↓ Wait up to 5 seconds ↓ No genuine removal occurred ↓ Stored device is still this device ↓ Continue original equipment processing The original menu-access behavior is therefore preserved. Fast removal without an animation OnUnequipped() ↓ Temporary EquipItem() ↓ OnEquipped() ↓ RemovalOperation = 1 ↓ Wait ↓ RemoveDevice() ↓ UnlockDevice() ↓ OnUnequipped() ↓ RemovalToken detected ↓ UnsetStoredDevice() ↓ RemovalOperation cleared ↓ Waiting OnEquipped resumes ↓ Stored device != current device + rendered device not equipped + rendered device absent from inventory ↓ return The obsolete OnEquipped() therefore never reaches the final EquipItem() calls. Removal while seated / animation path does not execute Removal request ↓ Animation path entered ↓ Animation path terminates without executing the animation ↓ RemoveDevice() ↓ Actual removal ↓ Waiting OnEquipped resumes ↓ Device is confirmed to be gone ↓ return This follows the same protection mechanism as other rapid-removal cases. 7. Exact Code-Level Changes Change 1 — Added a local state variable Original: int counter = 10 Final patch additionally introduces: bool waitingForRemoval = false and sets it when zad_RemovalOperation is active. Change 2 — Added an explicit default value The original uses: StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice ) The final patch uses: StorageUtil.GetIntValue( akActor, "zad_RemovalOperation" + zad_DeviousDevice, 0 ) This explicitly treats a missing value as zero. Change 3 — Added stale-event detection after the wait The original has no post-wait test that distinguishes a genuinely valid equipment event from an equipment event whose corresponding device has already been removed. The final version reads the existing stored inventory form and checks: storedDevice != deviceInventory && !akActor.IsEquipped(deviceRendered) && akActor.GetItemCount(deviceRendered) <= 0 When the condition is satisfied, the event is logged and immediately terminated. Change 4 — The actual OnUnequipped() removal mechanism is unchanged The final patch does not alter the normal removal sequence: akActor.RemoveItem(deviceRendered, 1, true) UnsetStoredDevice(akActor) OnRemoveDevice(akActor) ... StorageUtil.UnsetIntValue( akActor, "zad_RemovalToken" + deviceInventory ) Nor does it change the final clearing of: zad_RemovalOperation + zad_DeviousDevice in OnUnequipped(). The patch therefore does not replace the removal system itself. It only prevents an obsolete OnEquipped() invocation from continuing after that removal has already completed. 8. Improvement over the Earlier Test Patch An earlier test version used two additional persistent StorageUtil flags: zad_RemovalWait zad_RemovalCompleted The final patch removes those extra persistent states. Instead, it uses information DDNG already maintains: zad_Equipped..._Inventory actual rendered-device equipped state rendered-device inventory count This reduces the amount of patch-specific persistent state and avoids having separate wait/completion markers that could be overwritten or left behind when multiple equip/removal events overlap. 9. Features Intentionally Left Untouched The patch does not modify: quest-item removal protection OnUnequippedFilter() zad_RemovalToken zad_TightenToken zad_UntightenToken linked-device processing equipment conflict checks ShouldEquipSilently() OnEquippedPre() / OnEquippedPost() bound-effect handling the existing remove-all-items compatibility re-equip logic The patch is deliberately limited to discarding the obsolete OnEquipped() invocation created by the removal/re-equip race. 10. Core Difference The original behavior is effectively: "An OnEquipped() that was waiting for removal continues executing after that removal finishes." The final behavior is: "An OnEquipped() that was waiting for removal checks whether its device has actually disappeared. If it has, that old event is discarded." The patch therefore does not globally disable EquipItem(), does not disable UpdateControls(), and does not remove DD's intended menu-access re-equip behavior. It specifically limits the lifetime of the stale OnEquipped() event that causes the re-equipping race. Devious device NG 0.4.3 RE_Equip_Bug_fix.zip 35.64 kB · 1 download I just tested out your patch and tested it on two devices. They both removed without re-equipping. Thank you!
DeadOnKeyboard Posted September 20 Posted September 20 (edited) Patch for 1.7.104.0. The original mod is still required. DeviousDevicesNG-1.7.104.0-test.zip Edited September 20 by DeadOnKeyboard 3
Frayed Posted September 20 Posted September 20 On 9/12/2026 at 2:13 AM, DonQuiWho said: Thanks very much for the prompt, and succinct, reply! I'll pass that on, and see if that helps with this conundrum! DQW In addition to that: DD has two Armor entries for each restraint: one that is visible in the inventory and has a script attached to make it lockable, and one that is invisible from the inventory and is rendered on the character while equipped. zad_inventoryDevice should be on every script-carrying restraint Armor, and zad_Lockable on every corresponding lockable rendered Armor. The easiest way to check whether an item is a DD item is to check for those keywords. (Some devices, like plugs, are not lockable but do have scripts, and so will have zad_InventoryDevice on the inventory Armor but no zad_Lockable on the rendered Armor. However since they are not lockable, they are considered free to remove.) DD NG enforces restrained animations via C++ code (Ponzi's PAR or Partial Animation Replacer, shipped together with DD NG), bypassing the native animation system. DD 5.2 does not, and there are many ways to break its animations. zad_HeavyBondage is used for most devices that restrain the arms. These I think all have animations enforced via PAR. There are other devices like hobble dresses or blindfolds that have custom animations that are not enforced via PAR but instead via replacers. In DD NG these are done via OAR, but that means that they live inside OAR's priority system, so if other mods use absurdly high priorities they can overwrite DD NG's animations. Manually played animations will also overwrite OAR's idles. DD NG's zadLibs script has functions to get the inventory device of a given rendered device and visa versa. DD items do not play well with RemoveAllItems calls, unfortunately. Use of said function can lead to broken device states. I'm not sure about UnequipAll. 2
DonQuiWho Posted September 20 Posted September 20 3 hours ago, Frayed said: In addition to that: DD has two Armor entries for each restraint: one that is visible in the inventory and has a script attached to make it lockable, and one that is invisible from the inventory and is rendered on the character while equipped. zad_inventoryDevice should be on every script-carrying restraint Armor, and zad_Lockable on every corresponding lockable rendered Armor. The easiest way to check whether an item is a DD item is to check for those keywords. (Some devices, like plugs, are not lockable but do have scripts, and so will have zad_InventoryDevice on the inventory Armor but no zad_Lockable on the rendered Armor. However since they are not lockable, they are considered free to remove.) DD NG enforces restrained animations via C++ code (Ponzi's PAR or Partial Animation Replacer, shipped together with DD NG), bypassing the native animation system. DD 5.2 does not, and there are many ways to break its animations. zad_HeavyBondage is used for most devices that restrain the arms. These I think all have animations enforced via PAR. There are other devices like hobble dresses or blindfolds that have custom animations that are not enforced via PAR but instead via replacers. In DD NG these are done via OAR, but that means that they live inside OAR's priority system, so if other mods use absurdly high priorities they can overwrite DD NG's animations. Manually played animations will also overwrite OAR's idles. DD NG's zadLibs script has functions to get the inventory device of a given rendered device and visa versa. DD items do not play well with RemoveAllItems calls, unfortunately. Use of said function can lead to broken device states. I'm not sure about UnequipAll. Thanks very much Took the liberty of linking to that on TAK's version of AYGAS for his info DQW
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now