Scriptname zbfBondageShell extends Quest  

Actor Property PlayerRef Auto

Idle Property zbfIdleHandsBound Auto
Idle Property zbfIdleFree Auto

Keyword Property zbfEffectNoInventory Auto
keyword Property zbfEffectOpenMouth Auto
keyword Property zbfEffectWrist Auto
keyword Property zbfEffectNoSneak Auto
keyword Property zbfEffectNoMove Auto
keyword Property zbfEffectNoActivate Auto
keyword Property zbfEffectNoFighting Auto
keyword Property zbfEffectSetExpression Auto
keyword Property zbfEffectSlowMove Auto
Keyword Property zbfEffectNoMagic  Auto  
Keyword Property zbfEffectRefresh  Auto  

;  0: Dialogue Anger
;  1: Dialogue Fear
;  2: Dialogue Happy
;  3: Dialogue Sad
;  4: Dialogue Surprise
;  5: Dialogue Puzzled
;  6: Dialogue Disgusted
;  7: Mood Neutral
;  8: Mood Anger
;  9: Mood Fear
; 10: Mood Happy
; 11: Mood Sad
; 12: Mood Surprise
; 13: Mood Puzzled
; 14: Mood Disgusted
; 15: Combat Anger
; 16: Combat Shout
Int Property iExpressionType = 9 Auto
Int Property iExpressionStrength = 75 Auto

GlobalVariable Property zbfSettingUpdateInterval Auto
GlobalVariable Property zbfSettingSpeedMult Auto
GlobalVariable Property zbfSettingDisableEffects Auto
GlobalVariable Property zbfSettingCurrentVersion Auto

Int iAiRef = 0
Int iScriptVersion = 500

Event OnInit()
	UpgradeToLatest()
EndEvent

; Returns the version number multiplied by 100. V004 would return 400, small updates, 410, 420 etc.
Int Function GetVersion()
	; Returns iScriptVersion to prevent race conditions if upgrading two mods at the same time.
	Return iScriptVersion
EndFunction

Function UpgradeToLatest()
	If zbfSettingCurrentVersion.GetValueInt() != iScriptVersion
		If zbfSettingCurrentVersion.GetValueInt() > iScriptVersion
			Debug.Trace("zbf Downgrade?")
			;Debug.MessageBox("Zaz Animation Pack: Downgrading ...")

			zbfSettingCurrentVersion.SetValueInt(iScriptVersion)
		EndIf

		If zbfSettingCurrentVersion.GetValueInt() < 500
			Debug.Trace("zbf Upgrade to V005")
			;Debug.MessageBox("Zaz Animation Pack: Upgrade to V005")
			zbfSettingUpdateInterval.SetValue(0.5)
			zbfSettingDisableEffects.SetValueInt(0)
			zbfSettingCurrentVersion.SetValueInt(500)
		EndIf
		
		Stop()
		Start()
		zbfSettingCurrentVersion.SetValueInt(iScriptVersion)
	EndIf
EndFunction

; Return the speed multiplier when slowed by bondage
Float Function GetSpeedMult()
	Return zbfSettingSpeedMult.GetValue() As Float
EndFunction

; Returns the current update interval
Float Function GetUpdateInterval()
	Float fUpdateInterval = zbfSettingUpdateInterval.GetValue()
	If fUpdateInterval >= 0.1
		Return fUpdateInterval
	EndIf

	Return 0.5
EndFunction

; Ai reference counter, start ai control
Function RetainAi()
	iAiRef += 1
	Game.SetPlayerAiDriven(true)
EndFunction

; Ai reference counter, release ai control
Function ReleaseAi()
	If iAiRef > 1
		iAiRef -= 1
	Else
		Game.SetPlayerAiDriven(false)
		iAiRef = 0
	EndIf
EndFunction

; Updates control settings if akActor is the player
Function ApplyPlayerControls(Actor akActor)
	If akActor != PlayerRef
		Return
	EndIf
	
	; Inventory
	Bool disInventory = false
	If akActor.WornHasKeyword(zbfEffectNoInventory)
		disInventory = true
	EndIf

	; Wrist restraints
	bool disFighting = false
	bool disActivate = false
	If akActor.WornHasKeyword(zbfEffectNoActivate)
		disActivate = true
	EndIf

	If akActor.WornHasKeyword(zbfEffectNoFighting)
		disFighting = true
	EndIf

	; Ankle restraints
	Bool disSneaking = false
	If akActor.WornHasKeyword(zbfEffectNoSneak)
		disSneaking = true
	EndIf

	Bool disMovement = false
	If akActor.WornHasKeyword(zbfEffectNoMove)
		disMovement = true
	EndIf

	; Enable then disable all settings
	Game.EnablePlayerControls(abMovement = true, abFighting = true, abSneaking = true, abMenu = true, abActivate = true)
	Game.DisablePlayerControls(abMovement = disMovement, abFighting = disFighting, abSneaking = disSneaking, abMenu = disInventory, abActivate = disActivate)
EndFunction

; Checks only if mouth should be forced open
Function ApplyOpenMouthModifier(Actor akActor)
	int expressionStrength = 0
	int expressionType = 0

	; Override with gag mouth open
	If akActor.WornHasKeyword(zbfEffectOpenMouth)
		; expression set to shout at 100
		expressionStrength = 100
		expressionType = 16
	EndIf

	If (expressionType >= 0)
		akActor.SetExpressionOverride(aiMood = expressionType, aiStrength = expressionStrength)
	Else
		akActor.ClearExpressionOverride()
	EndIf
EndFunction

Function ApplyExpressionModifier(Actor akActor)
	int expressionStrength = 0
	int expressionType = 0

	; ExpressionType and Amount
	If akActor.WornHasKeyword(zbfEffectSetExpression)
		expressionStrength = iExpressionStrength
		expressionType = iExpressionType
	EndIf

	; Override with gag mouth open
	If akActor.WornHasKeyword(zbfEffectOpenMouth)
		; expression set to shout at 100
		expressionStrength = 100
		expressionType = 16
	EndIf

	If (expressionType >= 0)
		akActor.SetExpressionOverride(aiMood = expressionType, aiStrength = expressionStrength)
	Else
		akActor.ClearExpressionOverride()
	EndIf
EndFunction

; Updates properties on the actor which relates to movement 
Function ApplyMovementModifiers(Actor akActor)
	; Force slow movement on the actor - maybe should be some slowdown effect applied instead?
	If akActor.WornHasKeyword(zbfEffectSlowMove)
		akActor.SetAv("SpeedMult", GetSpeedMult())
	Else
		akActor.SetAv("SpeedMult", 100.0)
	EndIf
	akActor.ModAv("CarryWeight", -0.02)
	akActor.ModAv("CarryWeight", 0.02)
EndFunction

; Updates the idle to play when hands are bound
Function ApplyHandsModifier(Actor akActor)
	Idle kIdleToPlay = zbfIdleFree
	
	; Retrieve an idle to play
	If akActor.WornHasKeyword(zbfEffectWrist)
		kIdleToPlay = zbfIdleHandsBound
	EndIf
	
	akActor.PlayIdle(kIdleToPlay)
EndFunction

Function ApplySilenceEffect(Actor akActor)
	If akActor.WornHasKeyword(zbfEffectNoMagic)
		Spell kSpell = akActor.GetEquippedSpell(0)
		if kSpell != None
			akActor.UnequipSpell(kSpell, 0)
		EndIf
		kSpell = akActor.GetEquippedSpell(1)
		if kSpell != None
			akActor.UnequipSpell(kSpell, 1)
		EndIf
		
		Shout kShout = akActor.GetEquippedShout()
		If kShout != None
			akActor.UnequipShout(kShout)
		EndIf
	EndIf
EndFunction

; Apply all modifiers on the target, expression, movement, hands bound animation and player controls
Function ApplyAllModifiers(Actor akActor, Bool abApplyMouth = True, Bool abApplyMovement = True, Bool abApplySilence = True, Bool abApplyPlayerControls = True)
	If (zbfSettingDisableEffects.GetValue() as Int) == 0
		If abApplyPlayerControls
			ApplyPlayerControls(akActor)
		EndIf
		
		If abApplyMovement
			ApplyMovementModifiers(akActor)
		EndIf
		
		If abApplySilence
			ApplySilenceEffect(akActor)
		EndIf
		
		If abApplyMouth
			ApplyExpressionModifier(akActor)
		EndIf

		; ApplyHandsModifier is always handled separately as to not break animations
		; ApplyHandsModifier(akActor)
	Else
		ApplyOpenMouthModifier(akActor)
	EndIf
EndFunction

Bool Function HasExpressionModifier(Actor akActor)
	Return akActor.WornHasKeyword(zbfEffectOpenMouth) || akActor.WornHasKeyword(zbfEffectSetExpression)
EndFunction

Bool Function HasPlayerControls(Actor akActor)
	Bool hasPc = akActor.WornHasKeyword(zbfEffectNoInventory) || akActor.WornHasKeyword(zbfEffectNoActivate) || akActor.WornHasKeyword(zbfEffectNoFighting) || akActor.WornHasKeyword(zbfEffectNoSneak) || akActor.WornHasKeyword(zbfEffectNoMove)
	
	Return hasPc
EndFunction

Bool Function HasMovementModifier(Actor akActor)
	Return akActor.WornHasKeyword(zbfEffectSlowMove)
EndFunction

Bool Function HasSilenceEffect(Actor akActor)
	Return akActor.WornHasKeyword(zbfEffectNoMagic)
EndFunction
