2021-10-05 22:15:58 +00:00
Scriptname _00E_Theriantrophist_PlayerAsWerewolf extends ReferenceAlias
; Main script of the theriantrophist system; this alias contains the player during the transformation
; and while he is in werewolf form.
; this script is responsible for stats calculation, controlling the player as werewolf and propagation
; events/control signals to chymikum activemagiceffect scripts etc.
_00E_Theriantrophist_TransformStorage Property EquipementStorage Auto
_00E_Theriantrophist_WolfAttributes Property WolfAttributes Auto
_FS_TheriantrophistControlQuest Property ControlQuest Auto
Spell Property _00E_Theriantrophist_WolfFormSP Auto
{the spell that visually transforms the player}
GlobalVariable Property _00E_Theriantrophist_IsTransformed Auto
Perk Property _00E_Class_Theriantrophist_P06b_BeastInstinct Auto
{the perk that gives the player life detection when in wolf form}
Spell Property _00E_Theriantrophist_DetectLifeBaseSP Auto
{the life detection spell given if the player has the beast instinct perk}
Perk Property _00E_Class_Theriantrophist_P09b_Disguise Auto
Perk Property _00E_Class_Theriantrophist_P09c_AetherBlood Auto
Perk Property _00E_Class_Theriantrophist_P06c_Nightwolf_01 Auto
Perk Property _00E_Class_Theriantrophist_P06c_Nightwolf_02 Auto
Perk Property _00E_Class_Theriantrophist_P06c_Nightwolf_03 Auto
Idle Property WerewolfSneakStart Auto
Idle Property WerewolfSneakStop Auto
Weapon Property _00E_Theriantrophist_Claws Auto
Keyword Property _00E_Theriantrophist_NoTransformLocation Auto
Keyword Property _00E_Theriantrophist_NoTransformLocationTown Auto
Message Property _00E_Theriantrophist_ForceTransformBackMessage Auto
Message Property _00E_Theriantrophist_ForceTransformBackMessageTown Auto
GlobalVariable Property _00E_FS_Theriantrophist_AllowTemporaryTransform Auto
Spell Property _00E_FS_Affinity_AbBrute Auto
Spell Property _00E_FS_Affinity_AbDrifter Auto
Spell Property _00E_FS_Affinity_AbDruid Auto
Spell Property _00E_FS_Affinity_AbNightwolf Auto
Spell Property _00E_FS_Affinity_AbRavager Auto
Spell Property _00E_FS_Affinity_AbScourge Auto
Spell Property _00E_FS_Affinity_AbSoulcaller Auto
Spell Property _00E_FS_Affinity_ScourgeOfTheWilds_TitanSP Auto
Armor Property _00E_FS_Theriantrophist_Skin_Brute Auto
Armor Property _00E_FS_Theriantrophist_Skin_Drifter Auto
Armor Property _00E_FS_Theriantrophist_Skin_Druid Auto
Armor Property _00E_FS_Theriantrophist_Skin_Nightwolf Auto
Armor Property _00E_FS_Theriantrophist_Skin_Ravager Auto
Armor Property _00E_FS_Theriantrophist_Skin_Soulcaller Auto
Armor Property _00E_FS_Theriantrophist_Skin_Scourge Auto
Shout Property _00E_A1_Theriantrophist_TransformBack Auto
WordOfPower Property _00E_FS_Theriantrophist_TransformBack_Word01 Auto
Quest Property FS_NQ11 Auto
float fPlayerScale = 1.0
; Achievement
bool bTransformAchievementUnlocked = false
; Balancing data
Float Property AlchemyPercentage = 0.03 Autoreadonly Hidden
{all werewolf attributes are boosted by this percentage for each alchemy skill point the player has}
Float Property StaminaPercentage = 0.005 Autoreadonly Hidden
{all werewolf attributes are boosted by this percentage for each attribute point over 90 the player has; the atrribute is stamina per default, but may be switched to mana if the player has the special perk}
Float Property BaseDamageResist = 10 Autoreadonly Hidden
{without extra perks and stuff, the werewolf has this amount of damage resist, i.e. this percentage of every damage point is prevented}
Float Property BaseDamagePerSkillLevelInEquippedSpellsSchool = 0.33 Autoreadonly Hidden
{the base claw damage of the werewolf (damage before the boost factor is applied) uses the DPS of equipped weapons, or the player skill level in the magic school of an equipped spell.
This skill level is multiplied with BaseDamagePerSkillLevelInEquippedSpellsSchool and treated as a DPS value of a weapon after that}
Float Property BalancingDamageMalusPercent = 33 Autoreadonly Hidden
{the claw damage of the wolf is decreased by this percentage for balancing purposes}
Float Property BalancingHealthMalusPercent = 85 Autoreadonly Hidden
{the health of the wolf is decreased by this percentage for balancing purposes}
Float Property BalancingStaminaMalusPercent = 75 Autoreadonly Hidden
{the stamina of the wolf is decreased by this percentage for balancing purposes}
Float Property BoostFactorInfluenceOnArmor = 11 Autoreadonly Hidden
{the boost factor multiplied with this value is the wolf armor bonus for balancing purposes}
2024-02-19 15:14:54 +00:00
_00E_Theriantrophist_TransformSC Property transformEffect Auto
2021-10-05 22:15:58 +00:00
_00E_Theriantrophist_Chymikum[] RegisteredChymikums
bool Sneaking = false
2024-01-29 05:41:24 +00:00
int function _GetScriptVersion() Global
return 1
endFunction
2024-02-19 15:14:54 +00:00
Function Transform(_00E_Theriantrophist_TransformSC aTransformEffect = None, int duration = 0)
2021-10-05 22:15:58 +00:00
bool bWasParalyzed
Actor player = self.getActorReference()
2023-12-08 02:45:53 +00:00
while player.GetActorValue("Paralysis") == 1
2021-10-05 22:15:58 +00:00
bWasParalyzed = true
Utility.Wait(1)
endwhile
If bWasParalyzed
Utility.Wait(6)
EndIf
fPlayerScale = player.GetScale()
2024-02-14 06:17:21 +00:00
if SKSE.GetVersion()
EquipementStorage.saveEquippedItems()
else
EquipementStorage.saveEquippedItemsVanilla()
endif
2023-12-08 02:45:53 +00:00
float preTransformDmgResist = player.GetActorValue("damageResist")
2024-02-14 06:17:21 +00:00
2024-02-19 15:14:54 +00:00
transformEffect = aTransformEffect
2024-02-14 06:17:21 +00:00
if duration > 0
; Wolf Blood should dispel the wolf form on finish (as of 2.1)
; With SKSE, we equalize their duration. The 5 seconds offset serves as a failsafe in case the main effect ends without changing the race.
_00E_Theriantrophist_WolfFormSP.setNthEffectDuration(0, duration + 5)
endif
2021-10-05 22:15:58 +00:00
_00E_Theriantrophist_WolfFormSP.Cast(player)
WolfAttributes.SetInWolfForm(true)
player.additem(_00E_Theriantrophist_Claws, abSilent = true)
player.equipItem(_00E_Theriantrophist_Claws, abSilent = true)
_Init(preTransformDmgResist)
gotoState("Transfomed")
_TeachTransformBackTalent()
2024-01-10 00:46:13 +00:00
If !bTransformAchievementUnlocked
2021-10-05 22:15:58 +00:00
bTransformAchievementUnlocked = true
2022-08-03 21:19:58 +00:00
Steam.UnlockAchievement("END_WEREWOLF_01")
2021-10-05 22:15:58 +00:00
EndIf
2024-01-10 14:08:24 +00:00
If FS_NQ11.GetCurrentStageID() == 15 || FS_NQ11.GetCurrentStageID() == 17
2021-10-05 22:15:58 +00:00
FS_NQ11.SetObjectiveCompleted(20)
EndIf
EndFunction
2024-02-19 15:14:54 +00:00
Function TransformBackByTimer()
transformEffect = None
TransformBack()
endfunction
2021-10-05 22:15:58 +00:00
Function TransformBack()
If _00E_FS_Affinity_ScourgeOfTheWilds_TitanSP == None
_00E_FS_Affinity_ScourgeOfTheWilds_TitanSP = Game.GetFormFromFile(0x0102F19E, "Enderal - Forgotten Stories.esm") as Spell
EndIf
2024-02-14 06:17:21 +00:00
;MagicEffect _00E_FS_Affinity_ScourgeOfTheWilds_TitanME = Game.GetFormFromFile(0x0102F19F, "Enderal - Forgotten Stories.esm") as MagicEffect
2021-10-05 22:15:58 +00:00
Actor PlayerREF = self.getActorReference()
2024-02-19 14:11:54 +00:00
if ! PlayerREF
PlayerREF = Game.GetForm(0x14) as Actor
endif
2021-10-05 22:15:58 +00:00
If PlayerREF.dispelSpell(_00E_FS_Affinity_ScourgeOfTheWilds_TitanSP)
while PlayerREF.getscale() > fPlayerScale
Utility.Wait(0.05)
endwhile
EndIf
PlayerREF.dispelSpell(_00E_Theriantrophist_WolfFormSP)
2024-02-19 15:14:54 +00:00
if transformEffect
transformEffect.GotoState("ForcedFinish")
transformEffect.Dispel()
transformEffect = None
endif
2021-10-05 22:15:58 +00:00
Endfunction
Function OnWolfFormSpellEnd()
gotoState("")
Actor wolf = self.getActorReference()
self.clear()
wolf.removeSpell(_00E_Theriantrophist_DetectLifeBaseSP)
wolf.removeItem(_00E_Theriantrophist_Claws, 100, abSilent = true)
WolfAttributes.SetInWolfForm(false)
EquipementStorage.equipeItems()
_DispelAllChymikums()
EndFunction
Function TransformBackIfTransformed()
if (IsTransformed())
TransformBack()
EndIf
Endfunction
Bool Function IsTransformed()
return self.getActorReference() != None
Endfunction
Function _TeachTransformBackTalent()
if !Game.getPlayer().HasSpell(_00E_A1_Theriantrophist_TransformBack)
Game.getPlayer().addShout(_00E_A1_Theriantrophist_TransformBack)
Game.unlockWord(_00E_FS_Theriantrophist_TransformBack_Word01)
Endif
Endfunction
Function _UpdateChymikumTransformed()
Int i = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
RegisteredChymikums[i].OnWolfFormStart()
EndIf
i += 1
EndWhile
Endfunction
Function _UpdateChymikumTransformedBack()
Int i = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
RegisteredChymikums[i].OnWolfFormFinish()
EndIf
i += 1
EndWhile
Endfunction
Function _DispelAllChymikums()
Int i = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
RegisteredChymikums[i].Dispel()
EndIf
i += 1
EndWhile
Endfunction
Function RegisterChymikum(_00E_Theriantrophist_Chymikum value)
if (RegisteredChymikums.length != 5)
RegisteredChymikums = new _00E_Theriantrophist_Chymikum[5]
EndIf
Int index = RegisteredChymikums.Find(None)
RegisteredChymikums[index] = value
Endfunction
Function UnRegisterChymikum(_00E_Theriantrophist_Chymikum value)
Int index = RegisteredChymikums.Find(value)
if index != -1
RegisteredChymikums[index] = None
EndIf
Endfunction
Int Function CalcRegisteredChymikumCount()
Int i = 0
Int count = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
count += 1
EndIf
i += 1
EndWhile
return count
EndFunction
_00E_Theriantrophist_Chymikum Function CalcNthRegisteredChymikum(Int index)
Int i = 0
While i < RegisteredChymikums.length
if RegisteredChymikums[i] != None
if index == 0
return RegisteredChymikums[i]
Endif
index = index - 1
EndIf
i += 1
Endwhile
return None
EndFunction
Function _Init(float preTransformDmgResist)
_InitStats(preTransformDmgResist)
Actor player = self.getActorReference()
if (player.hasPerk(_00E_Class_Theriantrophist_P06b_BeastInstinct))
player.addSpell(_00E_Theriantrophist_DetectLifeBaseSP, false)
Endif
Endfunction
Function _InitStats(float preTransformDmgResist)
Actor player = self.getActorReference()
2023-12-08 02:45:53 +00:00
Float boostAttribute = player.GetActorValue("stamina") - 90
if player.hasPerk(_00E_Class_Theriantrophist_P09c_AetherBlood) && player.GetActorValue("magicka") - 88 > boostAttribute
boostAttribute = player.GetActorValue("magicka") - 88
2021-10-05 22:15:58 +00:00
EndIf
; damage, health and stamina in werewolf form are boosted by this factor
2023-12-08 02:45:53 +00:00
Float boostFactor = 1 + player.GetActorValue("alchemy") * AlchemyPercentage + boostAttribute * StaminaPercentage
Float boostFactorLifeStamina = 1 + player.GetActorValue("alchemy") * AlchemyPercentage
2021-10-05 22:15:58 +00:00
2023-12-08 02:45:53 +00:00
float fHealthMod = player.GetBaseActorValue("health") * boostFactorLifeStamina * (1 - BalancingHealthMalusPercent / 100.0)
float fStaminaMod = player.GetBaseActorValue("stamina") * boostFactorLifeStamina * (1 - BalancingStaminaMalusPercent / 100.0)
2021-10-05 22:15:58 +00:00
WolfAttributes.ModWolfHealth(fHealthMod)
WolfAttributes.ModWolfStamina(fStaminaMod)
2024-02-14 06:17:21 +00:00
float weaponStrength
if SKSE.GetVersion()
weaponStrength = _CalcWeaponSpellStrength(player)
else
weaponStrength = player.GetActorValue("OneHanded") / 2
endif
float fClawDamage = boostFactor * weaponStrength * (1 - BalancingDamageMalusPercent / 100.0)
2023-12-08 02:45:53 +00:00
float ClawDamageBonus = player.GetActorValue("LastBribedIntimidated")
2021-10-05 22:15:58 +00:00
WolfAttributes.ForceWolfUnarmedDmg(fClawDamage + ClawDamageBonus)
float fDamageResistBonus = BoostFactorInfluenceOnArmor * boostFactor
float fDamageResist = preTransformDmgResist + fDamageResistBonus
WolfAttributes.ForceWolfDamageResist(fDamageResist)
WolfAttributes.ForceWolfSpeedMult(100)
EndFunction
Float Function _CalcWeaponSpellStrength(Actor player)
Float result = EquipementStorage.getWeaponDamageSpeed()
if (EquipementStorage.isEquippedItemRightSpell())
result += _GuessPlayerSkillInSpellSchool(player, EquipementStorage.getEquippedSpellRight()) * BaseDamagePerSkillLevelInEquippedSpellsSchool
EndIf
if (EquipementStorage.isEquippedItemLeftSpell())
result += _GuessPlayerSkillInSpellSchool(player, EquipementStorage.getEquippedSpellLeft()) * BaseDamagePerSkillLevelInEquippedSpellsSchool
EndIf
return result
EndFunction
Float Function _GuessPlayerSkillInSpellSchool(Actor player, Spell s)
String guessedSchool = s.getNthEffectMagicEffect(0).getAssociatedSkill()
2023-12-08 02:45:53 +00:00
return player.GetActorValue(guessedSchool)
2021-10-05 22:15:58 +00:00
EndFunction
Int Function _CalcNumberOfPerksPlayerHas(Perk[] perks)
Actor player = self.getActorReference()
Int i = perks.length
Int perkCount = 0
while (i > 0)
i = i - 1
if (player.hasPerk(perks[i]))
perkCount += 1
EndIf
Endwhile
return perkCount
Endfunction
Function UpdateChymikumsLoadGame()
Int i = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
RegisteredChymikums[i].OnPlayerLoadGame()
EndIf
i += 1
EndWhile
EndFunction
Function UpdateChymikumOnCombatHit(Actor target)
Int i = 0
while (i < RegisteredChymikums.length)
if (RegisteredChymikums[i] != None)
RegisteredChymikums[i].OnCombatHit(target)
EndIf
i += 1
EndWhile
Endfunction
bool function CanBeTransformed(Location loc)
if loc && loc.hasKeyword(_00E_Theriantrophist_NoTransformLocation) && _00E_FS_Theriantrophist_AllowTemporaryTransform.GetValueInt() != 1
return false
elseif loc && loc.hasKeyword(_00E_Theriantrophist_NoTransformLocationTown) && !Game.getPlayer().hasPerk(_00E_Class_Theriantrophist_P09b_Disguise) && _00E_FS_Theriantrophist_AllowTemporaryTransform.GetValueInt() != 1
return false
endif
return true
Endfunction
Function _ReapplySkins()
2024-02-09 20:18:37 +00:00
if ! SKSE.GetVersion()
return
endif
Actor player = GetReference() as Actor
2024-02-10 07:56:23 +00:00
if !player || player.IsOnMount() || player.IsSwimming()
2024-02-09 20:18:37 +00:00
return
endif
2021-10-05 22:15:58 +00:00
ActorBase playerbase = player.GetActorBase()
if player.HasSpell(_00E_FS_Affinity_AbBrute)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Brute)
elseif player.HasSpell(_00E_FS_Affinity_AbDrifter)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Drifter)
elseif player.HasSpell(_00E_FS_Affinity_AbDruid)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Druid)
elseif player.HasSpell(_00E_FS_Affinity_AbNightwolf)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Nightwolf)
elseif player.HasSpell(_00E_FS_Affinity_AbRavager)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Ravager)
elseif player.HasSpell(_00E_FS_Affinity_AbScourge)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Scourge)
elseif player.HasSpell(_00E_FS_Affinity_AbSoulcaller)
playerbase.SetSkin(_00E_FS_Theriantrophist_Skin_Soulcaller)
else
playerbase.SetSkin(playerbase.GetRace().GetSkin())
endif
player.QueueNiNodeUpdate()
EndFunction
Function _FixNotTransformedPlayerSkin()
2024-02-09 20:18:37 +00:00
if ! SKSE.GetVersion()
return
endif
Actor player = GetReference() as Actor
2024-02-10 07:56:23 +00:00
if player && ! player.IsOnMount() && ! player.IsSwimming()
2024-02-09 20:18:37 +00:00
player.getActorBase().SetSkin(player.GetRace().GetSkin())
player.QueueNiNodeUpdate()
endif
2021-10-05 22:15:58 +00:00
EndFunction
Event OnLocationChange(Location akOldLoc, Location akNewLoc)
if !CanBeTransformed(akNewLoc)
if akNewLoc.hasKeyword(_00E_Theriantrophist_NoTransformLocationTown)
_00E_Theriantrophist_ForceTransformBackMessageTown.show()
else
_00E_Theriantrophist_ForceTransformBackMessage.show()
endif
RegisterForSingleUpdate(6)
Endif
Endevent
Event OnUpdate()
if !CanBeTransformed(self.GetRef().GetCurrentLocation())
TransformBackIfTransformed()
Endif
EndEvent
Function OnPlayerLoadGame()
WolfAttributes.renewNotPersistentStats()
UpdateChymikumsLoadGame()
2024-02-09 20:18:37 +00:00
_FixNotTransformedPlayerSkin()
2021-10-05 22:15:58 +00:00
Endfunction
State Transfomed
Event OnBeginState()
_00E_Theriantrophist_IsTransformed.setValue(1)
_UpdateChymikumTransformed()
RegisterForControl("Sneak")
SendModEvent("Theriantrophist_Transformed")
Sneaking = false
AddInventoryEventFilter(_00E_Theriantrophist_Claws)
Endevent
Event OnEndState()
if (Sneaking)
self.getActorReference().playIdle(WerewolfSneakStop)
WolfAttributes.SetTemporarilyWolfSpeedMultMod(0)
Sneaking = false
ControlQuest.GetAffinityControl().OnSneak(false)
Endif
_UpdateChymikumTransformedBack()
_00E_Theriantrophist_IsTransformed.setValue(0)
SendModEvent("Theriantrophist_TransformedBack")
UnRegisterForControl("Sneak")
Endevent
Function OnPlayerLoadGame()
WolfAttributes.renewNotPersistentStats()
UpdateChymikumsLoadGame()
RegisterForControl("Sneak")
_ReapplySkins()
Endfunction
Event OnControlDown(string control)
If Sneaking
self.getActorReference().playIdle(WerewolfSneakStop)
WolfAttributes.SetTemporarilyWolfSpeedMultMod(0)
Sneaking = false
ControlQuest.GetAffinityControl().OnSneak(false)
Else
self.getActorReference().playIdle(WerewolfSneakStart)
Actor akplayer = self.GetActorReference()
if akplayer.HasPerk(_00E_Class_Theriantrophist_P06c_Nightwolf_01)
WolfAttributes.SetTemporarilyWolfSpeedMultMod(-55)
elseif akplayer.HasPerk(_00E_Class_Theriantrophist_P06c_Nightwolf_02)
WolfAttributes.SetTemporarilyWolfSpeedMultMod(-50)
elseif akplayer.HasPerk(_00E_Class_Theriantrophist_P06c_Nightwolf_03)
WolfAttributes.SetTemporarilyWolfSpeedMultMod(-40)
else
WolfAttributes.SetTemporarilyWolfSpeedMultMod(-60)
endif
Sneaking = true
ControlQuest.GetAffinityControl().OnSneak(true)
EndIf
EndEvent
Event OnObjectEquipped(Form akBaseObject, ObjectReference akReference)
2024-02-13 09:53:17 +00:00
if akBaseObject != _00E_Theriantrophist_Claws
2021-10-05 22:15:58 +00:00
self.getActorReference().UnequipItem(akBaseObject)
self.getActorReference().equipItem(_00E_Theriantrophist_Claws, abSilent = true)
Endif
Endevent
Event OnObjectUnequipped(Form akBaseObject, ObjectReference akReference)
if (akBaseObject == _00E_Theriantrophist_Claws)
self.getActorReference().equipItem(_00E_Theriantrophist_Claws, abSilent = true)
Endif
Endevent
Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
if (akBaseItem != _00E_Theriantrophist_Claws)
return
Endif
If !akDestContainer
akItemReference.delete()
self.getActorReference().addItem(_00E_Theriantrophist_Claws, abSilent = true)
self.getActorReference().equipItem(_00E_Theriantrophist_Claws, abSilent = true)
Else
akDestContainer.removeItem(_00E_Theriantrophist_Claws)
self.getActorReference().addItem(_00E_Theriantrophist_Claws, abSilent = true)
self.getActorReference().equipItem(_00E_Theriantrophist_Claws, abSilent = true)
Endif
EndEvent
EndState