Skip to content

Commit

Permalink
New implementation of wrist restraints.
Browse files Browse the repository at this point in the history
This makes it much easier to implement new wrist restraints. No more
extra quests and extra effect scripts. Wrist restraints now use the
exact same code every other restraint is using. They no longer prevent
viewing the inventory, but you still cannot equip weapons or armor while
wearing one. This gives wrist restraints much more flexibility, as they
now also can use the standard escape system, allowing things like
lockable armbinders or cutting out of them, even.

Old devices should still work the old way. The only change to them is
that they also allow inventory access.
  • Loading branch information
TheKimy committed Apr 27, 2017
1 parent d31ce89 commit f91ed31
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 30 deletions.
Binary file modified 00 Core/Devious Devices - Integration.esm
Binary file not shown.
75 changes: 63 additions & 12 deletions 00 Core/scripts/Source/zadEquipScript.psc
Expand Up @@ -21,6 +21,10 @@ String Property deviceName Auto
Bool Property JammedLock = False Auto ; Is the lock currently jammed? (Deprecated, do NOT use!)
MiscObject Property Lockpick Auto

; Wrist Bondage and struggle system
Idle[] Property struggleIdles Auto
Idle[] Property struggleIdlesHob Auto

; Unlock system
Key Property deviceKey Auto ; Key type to unlock this device
Bool Property DestroyKey = False Auto ; If set to true, the key(s) will be destroyed when the device is unlocked or escaped from.
Expand Down Expand Up @@ -184,6 +188,12 @@ Event OnEquipped(Actor akActor)
RegisterForSleep()
OnEquippedPost(akActor)
SetLockShield()
If deviceRendered.HasKeyword(libs.zad_DeviousHeavyBondage)
libs.StartBoundEffects(akActor)
EndIf
LastEscapeAttemptAt = 0.0
LastUnlockAttemptAt = 0.0
LastRepairAttemptAt = 0.0
EndEvent


Expand Down Expand Up @@ -382,8 +392,7 @@ EndFunction

Function RemoveDevice(actor akActor, bool destroyDevice=false, bool skipMutex=false)
; cannot remove DD devices when wearing bondage mittens...except the bondage mittens!
; armbinders and yokes do NOT use this function to get removed, hence this works.
if (akActor.WornHasKeyword(libs.zad_DeviousBondageMittens) && !deviceRendered.HasKeyword(libs.zad_DeviousBondageMittens))
if !deviceRendered.HasKeyword(libs.zad_DeviousHeavyBondage) && (akActor.WornHasKeyword(libs.zad_DeviousBondageMittens) && !deviceRendered.HasKeyword(libs.zad_DeviousBondageMittens))
libs.NotifyPlayer("You cannot remove the " + deviceName + " while wearing bondage mittens!", true)
return
EndIf
Expand All @@ -404,6 +413,9 @@ Function RemoveDevice(actor akActor, bool destroyDevice=false, bool skipMutex=fa
Else
libs.RemoveDevice(akActor, deviceInventory, deviceRendered, zad_DeviousDevice, DestroyOnRemove, skipMutex=skipMutex)
Endif
If deviceRendered.HasKeyword(libs.zad_DeviousHeavyBondage)
libs.StopBoundEffects(akActor)
EndIf
If akActor != Libs.PlayerRef
return
EndIf
Expand All @@ -429,6 +441,10 @@ bool Function RemoveDeviceWithKey(actor akActor = none, bool destroyDevice=false
If !CheckLockShield() ; is the timer expired?
Return False
EndIf
; Check if she is able to unlock herself. We do this check here to allow it to apply even to keyless restraints that shouldn't be just removed.
If !CheckLockAccess()
Return False
EndIf
If DeviceKey
If libs.PlayerReF.GetItemCount(DeviceKey) <= 0
If zad_DD_OnNoKeyMSG
Expand All @@ -444,11 +460,7 @@ bool Function RemoveDeviceWithKey(actor akActor = none, bool destroyDevice=false
libs.Notify("You do not posess enough keys to manipulate this " + deviceName + ".")
EndIf
Return False
EndIf
; Check if she is able to unlock herself:
If !CheckLockAccess()
Return False
EndIf
EndIf
; The key break chance defaults to zero, so we don't need to check for quest items etc. If modders set this chance higher, it's their responsibility!
If Utility.RandomFloat(0.0, 99.9) < KeyBreakChance
Libs.PlayerRef.RemoveItem(DeviceKey, Utility.RandomInt(1, NumberOfKeysNeeded))
Expand Down Expand Up @@ -636,12 +648,16 @@ Bool Function CheckLockAccess()
Return False
EndIf
If Utility.RandomFloat(0.0, 99.9) < LockAccessDifficulty
If LockAccessDifficulty < 50.0
libs.notify("You try to insert the key into the " + DeviceName + "'s lock, but find the locks a bit outsides of your reach. After a few failed attempts to slide the key into the lock, you have no choice but to give up for now. You should still eventually be able to unlock yourself. Just try again a bit later!", messageBox = True)
ElseIf LockAccessDifficulty < 100.0
libs.notify("This restraint was designed to make it hard for the person locked it in to unlock herself. You struggle hard trying to insert the key into the " + DeviceName + "'s lock anyway, but find the locks well outsides of your reach. Tired from your struggles, you have no choice but to give up for now. Maybe try again later!", messageBox = True)
If DeviceKey
If LockAccessDifficulty < 50.0
libs.notify("You try to insert the key into the " + DeviceName + "'s lock, but find the locks a bit outsides of your reach. After a few failed attempts to slide the key into the lock, you have no choice but to give up for now. You should still eventually be able to unlock yourself. Just try again a bit later!", messageBox = True)
ElseIf LockAccessDifficulty < 100.0
libs.notify("This restraint was designed to make it hard for the person locked it in to unlock herself. You struggle hard trying to insert the key into the " + DeviceName + "'s lock anyway, but find the locks well outsides of your reach. Tired from your struggles, you have no choice but to give up for now. Maybe try again later!", messageBox = True)
Else
libs.notify("This restraint was designed to put the locks safely out of reach of the person wearing it. There is no way you will ever be able to unlock yourself, even when in possession of the proper key. You will need to seek help!", messageBox = True)
EndIf
Else
libs.notify("This restraint was designed to put the locks safely out of reach of the person wearing it. There is no way you will ever be able to unlock yourself, even when in possession of the proper key. You will need to seek help!", messageBox = True)
libs.notify("You try to undo your restraint, but you are unable to reach the locking mechanism!", messageBox = True)
EndIf
Return False
EndIf
Expand Down Expand Up @@ -680,6 +696,9 @@ Function DeviceMenuExt(Int msgChoice)
EndFunction

Function DeviceMenuEquip()
if libs.PlayerRef.IsEquipped(DeviceRendered) || libs.PlayerRef.WornHasKeyword(zad_DeviousDevice)
return
EndIf
EquipDevice(libs.PlayerRef)
libs.NotifyPlayer("You choose to put the " + deviceName + " on.")
EndFunction
Expand Down Expand Up @@ -1147,6 +1166,37 @@ Float Function CalclulateLockPickSuccess()
return result
EndFunction

Idle[] Function SelectStruggleArray(actor akActor)
If akActor.WornHasKeyword(libs.zad_DeviousHobbleSkirt) && !akActor.WornHasKeyword(libs.zad_DeviousHobbleSkirtRelaxed)
if struggleIdlesHob.length > 0
return struggleIdlesHob ; Use hobbled struggle idles
else
return struggleIdles ; Fall back to standard animations if no hobbled variants are available
endif
Else
return struggleIdles ; Use regular struggle idles
Endif
EndFunction

Function StruggleScene(actor akActor)
if libs.IsAnimating(akActor)
return
EndIf
Idle[] struggleArray = SelectStruggleArray(akActor)
int len = struggleArray.length - 1
If len < 1
return
EndIf
bool[] cameraState = libs.StartThirdPersonAnimation(akActor, struggleArray[Utility.RandomInt(0, len)], true)
Utility.Wait(2.5)
libs.Pant(libs.PlayerRef)
Utility.Wait(2.5)
akActor.PlayIdle(struggleArray[Utility.RandomInt(0, len)])
Utility.Wait(5)
libs.EndThirdPersonAnimation(akActor, cameraState, true)
libs.SexlabMoan(libs.PlayerRef)
EndFunction

Function EscapeAttemptStruggle()
If !CanMakeEscapeAttempt()
return
Expand All @@ -1156,6 +1206,7 @@ Function EscapeAttemptStruggle()
Else
libs.zad_DD_EscapeStruggleMSG.Show()
EndIf
StruggleScene(libs.PlayerRef)
Int i = Escape(CalclulateStruggleSuccess())
If i == 1
; device got removed in Escape(), so just need to show the success message.
Expand Down
140 changes: 135 additions & 5 deletions 00 Core/scripts/Source/zadLibs.psc
Expand Up @@ -15,6 +15,8 @@ zadEventSlots Property EventSlots Auto ; See zadBaseEvent.psc for how to use the
zadDevicesUnderneathScript Property DevicesUnderneath Auto
Quest Property zadNPCSlots Auto
zadBoundCombatScript Property BoundCombat auto
Int Property TweenMenuKey Auto
bool Property Terminate Auto

zbfBondageShell Property zbf Auto
zbfPlayerControl Property zbfPC Auto
Expand Down Expand Up @@ -1317,11 +1319,11 @@ EndFunction


float Function GetVersion()
return 6 ; build number increment to determine the newest version - does NOT correspond with the offical version name. Returns a float not to mess with existing implementations of this function.
return 7 ; build number increment to determine the newest version - does NOT correspond with the offical version name. Returns a float not to mess with existing implementations of this function.
EndFunction

String Function GetVersionString()
return "3.3" ; string to be displayed in MCM etc.
return "3.4" ; string to be displayed in MCM etc.
EndFunction


Expand Down Expand Up @@ -2215,11 +2217,12 @@ Function UpdateControls()
EndIf
if IsBound(playerRef)
If playerRef.WornHasKeyword(zad_BoundCombatDisableKick)
fighting = false
fighting = false
Else
fighting = config.UseBoundCombat
fighting = config.UseBoundCombat
Endif
menu = !config.HardcoreEffects
sneaking = false
; menu = !config.HardcoreEffects
EndIf
zbfPC.SetDisabledControls(abMovement = !movement, abFighting = !fighting, abSneaking = !sneaking, abMenu = !menu, abActivate = !activate)
EndFunction
Expand Down Expand Up @@ -2783,6 +2786,133 @@ string Function LookupDeviceType(keyword kwd)
Error("LookupDeviceType received invalid keyword " + kwd)
EndFunction

bool function hasAnyWeaponEquipped(actor a)
if !a.GetEquippedWeapon(true) && !a.GetEquippedWeapon(false) && !a.getEquippedSpell(1) && !a.getEquippedSpell(0)
return false
endif
return true
EndFunction

function stripweapons(actor a, bool unequiponly = true)
int i = 2
Spell spl
Weapon weap
Armor sh
While i > 0
i -= 1
if i == 0
Utility.Wait(1.0)
EndIf
spl = a.getEquippedSpell(1)
if spl
a.unequipSpell(spl, 1)
endIf
weap = a.GetEquippedWeapon(true)
if weap
a.unequipItem(weap, false, true)
endIf
sh = a.GetEquippedShield()
if sh
a.unequipItem(sh, false, true)
endIf
spl = a.getEquippedSpell(0)
if spl
a.unequipSpell(spl, 0)
endIf
weap = a.GetEquippedWeapon(false)
if weap
a.unequipItem(weap, false, true)
endIf
EndWhile
endfunction

Event StartBoundEffects(Actor akTarget)
while hasAnyWeaponEquipped(akTarget)
stripweapons(akTarget)
EndWhile
if akTarget != PlayerRef
BoundCombat.Apply_ABC(akTarget)
BoundCombat.Apply_NPC_ABC(akTarget)
return
EndIf
Log("OnEffectStart(): Wrist Bondage")
if aktarget == PlayerRef
Terminate = False
EndIf
PlayBoundIdle()
DoRegister()
if aktarget == PlayerRef
UpdateControls()
Endif
EndEvent

Event StopBoundEffects(Actor akTarget)
Log("OnEffectFinish(): Wrist Bondage")
Debug.SendAnimationEvent(akTarget, "IdleForceDefaultState")
if aktarget == PlayerRef
Terminate = True
UpdateControls()
else
BoundCombat.Remove_NPC_ABC(akTarget)
EndIf
BoundCombat.Remove_ABC(akTarget)
EndEvent

Function DoRegister()
if !Terminate
RegisterForSingleUpdate(8.0)
EndIf
EndFunction

Function DoUnregister()
if !Terminate
UnregisterForUpdate()
EndIf
EndFunction

Function DoReLoad()
if PlayerRef.WornHasKeyword(zad_DeviousHeavyBondage) && !Terminate
PlayBoundIdle()
DoRegister()
EndIf
EndFunction

Function DoUnLoad()
DoUnregister()
EndFunction

Event OnUpdate()
if ((Game.IsMenuControlsEnabled() && config.HardcoreEffects) || Game.IsFightingControlsEnabled())
if !IsAnimating(PlayerRef)
UpdateControls()
EndIf
EndIf
DoRegister()
EndEvent

Function PlayBoundIdle()
BoundCombat.Apply_ABC(PlayerRef)
if !Terminate && !IsAnimating(PlayerRef) && !PlayerRef.IsInFaction(SexLabAnimatingFaction)
ApplyBoundAnim(PlayerRef)
EndIf
EndFunction

Event OnCellLoad()
DoReLoad()
EndEvent

Event OnCellAttach()
DoReLoad()
EndEvent

Event OnLoad()
DoReLoad()
EndEvent

Event OnCellDetach()
DoUnLoad()
EndEvent

;===============================================================================
; GameSettings Manipulation
;===============================================================================
Expand Down
30 changes: 17 additions & 13 deletions 00 Core/scripts/Source/zadPlayerScript.psc
Expand Up @@ -6,6 +6,7 @@ zadCameraState Property cameraState Auto

zadLibs Property libs Auto
Formlist Property SitBlockKeywords Auto
Armor Property zad_DeviceHider Auto

Event OnPlayerLoadGame()
actor akActor = libs.PlayerRef
Expand Down Expand Up @@ -83,6 +84,12 @@ Event OnLocationChange(Location akOldLoc, Location akNewLoc)
EndIf
EndEvent

bool Function isDeviousDevice(Form device)
if device.HasKeyword(libs.zad_InventoryDevice) || device.HasKeyword(libs.zad_Lockable)
return true
endif
return false
EndFunction

Event OnObjectEquipped(Form akBaseObject, ObjectReference akReference)
actor akActor = libs.PlayerRef
Expand All @@ -97,18 +104,15 @@ Event OnObjectEquipped(Form akBaseObject, ObjectReference akReference)
EndIf
EndIf
EndIf
if akActor.WornHasKeyword(libs.zad_DeviousHeavyBondage) && ((akBaseObject as Weapon) || (akBaseObject as Spell) || (akBaseObject as Light) || (akBaseObject as Armor).HasKeywordString("ArmorShield"))
if akActor.GetEquippedItemType(0) > 0
akActor.UnequipItem((akActor.GetEquippedObject(0) as Weapon), abSilent = true)
akActor.UnequipItem((akActor.GetEquippedObject(0) as Armor), abSilent = true)
akActor.UnequipItem((akActor.GetEquippedObject(0) as Light), abSilent = true)
akActor.UnequipSpell((akActor.GetEquippedObject(0) as Spell), 0)
endif
if akActor.GetEquippedItemType(1) > 0
akActor.UnequipItem((akActor.GetEquippedObject(1) as Weapon), abSilent = true)
akActor.UnequipSpell((akActor.GetEquippedObject(1) as Spell), 1)
endif
endif
If akActor.WornHasKeyword(libs.zad_DeviousHeavyBondage) && ((akBaseObject as Weapon) || (akBaseObject as Spell) || (akBaseObject as Light) || ((akBaseObject as Armor) && (!isDeviousDevice(akBaseObject) && (akBaseObject != zad_DeviceHider))))
If UI.IsMenuOpen("InventoryMenu")
libs.notify("You can't equip this with your hands tied!")
Endif
libs.playerRef.UnequipItem(akBaseObject)
while libs.hasAnyWeaponEquipped(libs.playerRef)
libs.stripweapons(libs.playerRef)
EndWhile
Endif
EndEvent


Expand All @@ -124,5 +128,5 @@ Event OnObjectUnequipped(Form akBaseObject, ObjectReference akReference)
libs.HideBelly(libs.PlayerRef)
EndIf
EndIf
EndIf
EndIf
EndEvent
Binary file modified 00 Core/scripts/zadEquipScript.pex
Binary file not shown.
Binary file modified 00 Core/scripts/zadLibs.pex
Binary file not shown.
Binary file modified 00 Core/scripts/zadPlayerScript.pex
Binary file not shown.

0 comments on commit f91ed31

Please sign in to comment.