Jump to content

[LE] How to properly script a custom conditional system which will be used by a quest script?


Recommended Posts

Hello there, thanks for taking a look at my help request here :smile:

 

I'm currently trying to make a script which contains a custom conditional system which will be used by a particular quest script. The quest script is about a transformation feature which transform the player into something, and that transformation effect is triggered when some conditions are met. Those conditions are contained inside something i called "custom conditional system" above. To make things clearer and easy to read, i'll try to arrange it better here.

 

Transformation

  • Transform the player into "lost control" state
  • Lost control state replaces the player with a set of prepared NPCs which will be choosen depend on what weapons the player currently wield before being transformed
  • The replacer NPC is driven by an AI to attack and kill everything near it
  • The transformation effect has a calculated duration which is calculated from something called "killcount". Killcount checks how many kills the replacer NPC made after being transformed. The more it kills, the longer the duration.
  • Trigger for this whole transformation effect depends on calculations contained in the Transform Condition System

Transform Condition System

  • Has something i call "Condition meter" which has ranged values from 0 to 1000
  • Condition meter 1000 means 100% chance for the transformation effect to happen. Condition meter 0 means 0% chance to transform.
  • Condition meter will be filled or be emptied based on some game statistics' values. Some game statistics increase the condition meter, but some others reduce the meter.
  • Each game statistic is given a base value to add or subtract the condition meter.
  • Whenever the player do something which contribute to a particular game statistic, the base value of that game statistic will be contributed into the condition meter (either add it or subtract it).
  • The amount of condition meter will then determine the chance for the player to transform into lost control state.

I already have the base script for Transformation, but still currently trying to make Transform Condition System. I'm not sure whether it has to be written into its own script or can be included within Transformation script. If it has to be made separately, then do i need to add "extends Quest Conditional" or simply "extends Quest" (i'm still confused on the usage of Conditional keyword in scriptname line). Also, do i need to extends the script into Quest script or instead into some other script?

 

It will also be cool if someone would help me to write the base for Transform Condition System script :teehee:

 

I'm still new to papyrus scripting, so any guides will be appreciated. Thanks in advance :smile:

Link to comment
Share on other sites

It turns out that i have managed to solve most of them. There are still some works need to be done though, such as 1) making sure that Transformation only works when the player wears specific armor AND only in combat with health below xx% (configurable), 2) making codes to copy player's weapons into the replacer NPC's inventory, 3) adding "area fear effect" around the replacer NPC so that any innocent/weak NPCs will run away instead of trying to fight the replacer NPC. So, i'll still accept any helps for these areas.

 

Oh, i also want to notify that the scripts i'm using are based on Moonlight Tales Essentials Overhauled (MTEO) by ubuntufreakdragon. He gave me permission to use his scrips as long as i credited him. So all credits go to him since he has done most of the work :smile:

What i did was simply replacing his "Lunar Transformation" system with Transform Condition System (in which i renamed it into Morality System), replacing "feed" mechanism into "kill" mechanism (since the replacer NPCs are humanoids, not werewolves), replacing all instances about werewolf NPC replacer into my NPC replacer (named "Berserker"), removing any codes related to skin changer/updater, renaming properties and variables to suit my needs, and some minor things i couldn't remember. I also decided to give a name to Transformation mechanism with Lost Control (just like what MTEO uses).

 

I'll post my WIP script here so that people will get a better idea on what i'm working on (a bit long, but can't be helped lol)

Transformation (a.k.a. Lost Control) script (contains Morality System which is now built in into it):

 

 

Scriptname Berserk_Loose_Control_Script extends Quest Conditional

; -- PROPERTIES -- 

Quest Property Berserk_Quest_Morality Auto				;as a factor for loose control roll, referenced for morality, name can be changed to suit better

GlobalVariable Property TimeScale Auto
GlobalVariable Property GameDaysPassed Auto
GlobalVariable Property Berserk_Config_LooseControlMoral Auto				;a bool which check whether moral transform which lead to lost control is on or off, configurable via MCM
GlobalVariable Property Berserk_Config_LooseControlBaseChance Auto 
GlobalVariable Property Berserk_Config_LooseControlBaseWeight Auto
GlobalVariable Property Berserk_Config_LooseControlMoralWeight Auto			;default is 1, make sure it's counted correctly in the MCM
GlobalVariable Property Berserk_Config_LooseControlKillWeight Auto
GlobalVariable Property Berserk_Config_Moralitymax Auto

GlobalVariable Property Berserk_Config_Killscorebase Auto
GlobalVariable Property Berserk_Config_Killscoremodifier Auto
GlobalVariable Property Berserk_Config_Killscoremax Auto
GlobalVariable Property Berserk_Config_Killscoretimemultmax Auto
GlobalVariable Property Berserk_Config_LooseControlBaseTimeMin Auto
GlobalVariable Property Berserk_Config_LooseControlBaseTimeMax Auto
GlobalVariable Property Berserk_Config_LooseControlCooldownTimeMin Auto
GlobalVariable Property Berserk_Config_LooseControlCooldownTimeMax Auto

GlobalVariable Property Berserk_Moral_Config_GSWerewolf Auto				;default 10
GlobalVariable Property Berserk_Moral_Config_GSNeckBit Auto					;default 8
GlobalVariable Property Berserk_Moral_Config_GSBribe Auto					;default 1 
GlobalVariable Property Berserk_Moral_Config_GSIntimidation Auto			;default 4 
GlobalVariable Property Berserk_Moral_Config_GSTGQuest Auto					;default 5
GlobalVariable Property Berserk_Moral_Config_GSDBQuest Auto					;default 10 
GlobalVariable Property Berserk_Moral_Config_GSDaedricQuest Auto			;default 100 
GlobalVariable Property Berserk_Moral_Config_GSPeopleKill Auto				;default 2
GlobalVariable Property Berserk_Moral_Config_GSAnimalKill Auto				;default 2
GlobalVariable Property Berserk_Moral_Config_GSUndeadKill Auto				;default -2
GlobalVariable Property Berserk_Moral_Config_GSDaedraKill Auto				;default -4
GlobalVariable Property Berserk_Moral_Config_GSBunnyKill Auto				;default 1
GlobalVariable Property Berserk_Moral_Config_GSDragonSoul Auto				;default -2
GlobalVariable Property Berserk_Moral_Config_GSShoutLearn Auto				;default -4
GlobalVariable Property Berserk_Moral_Config_GSShoutMaster Auto				;default -10
GlobalVariable Property Berserk_Moral_Config_GSTotalBounty Auto				;default 80
GlobalVariable Property Berserk_Moral_Config_GSItemPickpocket Auto			;default 1
GlobalVariable Property Berserk_Moral_Config_GSTimeJail Auto				;default 4
GlobalVariable Property Berserk_Moral_Config_GSJailEscape Auto				;default 10
GlobalVariable Property Berserk_Moral_Config_GSItemSteal Auto				;default 1
GlobalVariable Property Berserk_Moral_Config_GSAssault Auto					;default 60
GlobalVariable Property Berserk_Moral_Config_GSMurder Auto					;default 80
GlobalVariable Property Berserk_Moral_Config_GSHorseSteal Auto				;default 4
GlobalVariable Property Berserk_Moral_Config_GSTrespass Auto				;default 2
GlobalVariable Property Berserk_Moral_Config_BountyStage1 Auto				;default 1000
GlobalVariable Property Berserk_Moral_Config_BountyStage2 Auto				;default 2000
GlobalVariable Property Berserk_Moral_Config_BountyStage3 Auto				;default 3000
GlobalVariable Property Berserk_Moral_Config_BountyStage4 Auto				;default 4000
GlobalVariable Property Berserk_Moral_Config_BountyStage5 Auto				;default 5000
GlobalVariable Property Berserk_Moral_Config_BountyStage6 Auto				;default 6000
GlobalVariable Property Berserk_Moral_Config_BountyStage7 Auto				;default 7000
GlobalVariable Property Berserk_Moral_Config_BountyStage8 Auto				;default 8000
GlobalVariable Property Berserk_Moral_Config_BountyStage9 Auto				;default 9000
GlobalVariable Property Berserk_Moral_Config_BountyStage10 Auto				;default 10000

GlobalVariable Property Berserk_Config_LooseControlDeathtype Auto

FormList Property Berserk_List_secure_caves Auto

Spell Property Berserk_Spell_Warningspell Auto					;similar with MTE, but only take visual effects without sound effects

Static Property DwePlatMid01 auto
ReferenceAlias Property BerserkerAlias auto
ActorBase Property Berserk_Player_Loose_Controls_1H_M auto
ActorBase Property Berserk_Player_Loose_Controls_2H_M auto
ActorBase Property Berserk_Player_Loose_Controls_Ranger_M auto
ActorBase Property Berserk_Player_Loose_Controls_Spellsword_M auto
ActorBase Property Berserk_Player_Loose_Controls_Mage_M auto
ActorBase Property Berserk_Player_Loose_Controls_Unarmed_M auto
ActorBase Property Berserk_Player_Loose_Controls_1H_F auto
ActorBase Property Berserk_Player_Loose_Controls_2H_F auto
ActorBase Property Berserk_Player_Loose_Controls_Ranger_F auto
ActorBase Property Berserk_Player_Loose_Controls_Spellsword_F auto
ActorBase Property Berserk_Player_Loose_Controls_Mage_F auto
ActorBase Property Berserk_Player_Loose_Controls_Unarmed_F auto

ImageSpaceModifier Property IllusionBlueMassiveImod Auto

ImageSpaceModifier Property FadeToBlackImod Auto
ImageSpaceModifier Property FadeToBlackHoldImod Auto
ImageSpaceModifier Property FadeToBlackBackImod Auto

Bool Property player_has_control=True auto conditional
Bool Property player_is_loosing_control=False auto conditional


; -- VARIABLEs -- 

Actor BerserkerRef
ObjectReference PlatRef
Int killscore = 0
Int killscorebuff = 0
Float killscorebuffresettime = 0.0
Bool LC_system_active = False
Float Invisibility_originalvalue = 0.0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Threadsafety
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Int mutex_control_status_turn =0
Int mutex_control_status_number =0
Function mutex_control_status_wait()
  ;atomic as read/write to script's own vars is atomic, Skyrim runs each Script only once at a time, and internal calls and edits won't break this run
  Int local_number=mutex_control_status_number
  mutex_control_status_number+=1
  while mutex_control_status_turn != local_number
    utility.wait(0.1)
  endwhile
  ;endatomic
EndFunction
Function mutex_control_status_signal()
  ;atomic as read/write to script's own vars is atomic, Skyrim runs each Script only once at a time, and internal calls and edits won't break this run
  mutex_control_status_turn+=1
  ;endatomic
EndFunction
  
  
; -- EVENTs -- 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;On Update Game Time, only running while moraltransformations runnin the main rolls
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 

Event OnUpdateGameTime() 
  ObjectReference PlayerRef = Game.GetPlayer()
  If !LC_system_active
    return
  EndIf
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;Loose Control check
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  If (Berserk_Config_LooseControlMoral.GetValue() as int == 1 && player_has_control && !player_is_loosing_control)
    Loose_Control_Roll()

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;Loose Control
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ElseIf player_is_loosing_control                          ;&& !PlayerRef.HasMagicEffect(Berserk_Effect_MoralTransformation);make sure to only call this once transformation is complete
    Trigger_control_loss()
    player_is_loosing_control = False
    PlayerRef.dispelspell(Berserk_Spell_Warningspell)
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;Gain Control back
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ElseIf !player_has_control
    update_player_pos ()
    Gaincontrol()
    If LC_system_active
      Float Min = Berserk_Config_LooseControlCooldownTimeMin.GetValue()
      Float Max = Berserk_Config_LooseControlCooldownTimeMax.GetValue()
      If Min >= Max
        Max = Min
      EndIf
      RegisterForSingleUpdateGameTime(utility.randomFloat(RealTimeSecondsToGameTimeHours(Min),RealTimeSecondsToGameTimeHours(Max)))
    EndIf
  EndIf
EndEvent
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;On Update, only running while control is actually lost
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
 
Event OnUpdate()
  If player_has_control
    return
  Endif
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;Update Playerpos to load cells
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  update_player_pos ()
  
  utility.wait(0.1) ;to let the new cell load if any
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;Monitor Health
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  update_player_health ();run after update pos, as unloaded NPC's have well unknown health values.
  
  
  RegisterForSingleUpdate(2.9)
EndEvent


; -- FUNCTIONs --

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Player got killed without Control
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function player_replacement_was_killed(Actor the_killed_replacement, Actor akKiller)
  ObjectReference PlayerRef = Game.GetPlayer()
  mutex_control_status_wait()
  ;debug.messagebox(player_has_control)
  ;debug.messagebox(BerserkerRef)
  ;debug.messagebox(the_killed_replacement)
  If player_has_control ;just ignore it, the player has the controls
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  If the_killed_replacement != BerserkerRef ;just ignore it, its the wrong replacement, maybe an older one not deleted yet, or something spawned by an external script, at least this should never become True
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  If BerserkerRef == None ;just ignore it, None should not die
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  
  PlayerRef.stoptranslation();just to be sure
  PlayerRef.moveto(BerserkerRef)
  ;PlayerRef.SetPosition(BerserkerRef.X, BerserkerRef.Y, BerserkerRef.Z)
  PlatRef.DisableNoWait()
  PlatRef.Delete()
  PlatRef = none
  Game.SetCameraTarget(PlayerRef)
  ;Debug.SetGodMode(False)
  PlayerRef.SetActorValue("Invisibility",Invisibility_originalvalue)
  PlayerRef.SetAlpha(1)
  Int deathtype = Berserk_Config_LooseControlDeathtype.GetValue() as int
  If deathtype == 0
    PlayerRef.setghost(False)
    PlayerRef.kill(akKiller)
  Endif
  BerserkerAlias.Clear()		
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)
  BerserkerRef.DisableNoWait()
  BerserkerRef.Delete()
  BerserkerRef=none
  game.setplayeraidriven(False)
  player_has_control = True
  UnRegisterForUpdate()
  mutex_control_status_signal() ;mutex.signal()
  If deathtype == 1
    wake_up_in_cave()
  Endif
EndFunction

Function wake_up_in_cave()
  ObjectReference PlayerRef = Game.GetPlayer()
  ;Todo some eye candy anim
  FadeToBlackImod.Apply()
  ;Debug.SendAnimationEvent(PlayerREF, "BreviMoonlightTalesRestS1")
  Utility.Wait(2.1)
  FadeToBlackImod.PopTo(FadeToBlackHoldImod)
  Int Target = utility.randomint(0,Berserk_List_secure_caves.getsize() - 1)
  Formlist Targetlocation = Berserk_List_secure_caves.getat(Target) as Formlist
  Objectreference TargetMarker = Targetlocation.getat(0) as Objectreference  ;get the Berserkermarker which is the first object in the cavedata
  PlayerRef.moveto(TargetMarker)
  Float whealth= 50.0 ;heal you up to 50 health
  Float phealth= PlayerRef.GetActorValue("health")
  Float diff = phealth-whealth
  If diff > 0
    PlayerRef.damageactorvalue("health",diff)
  ElseIf diff < 0
    PlayerRef.restoreactorvalue("health",-diff)
  EndIf
  Utility.Wait(0.5)
  PlayerRef.setghost(False)
  FadeToBlackHoldImod.PopTo(FadeToBlackBackImod)
  Utility.Wait(3)
  FadeToBlackImod.Remove()
  FadeToBlackHoldImod.Remove()
  FadeToBlackBackImod.Remove()
Endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function Player has killed on something: 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function player_has_killed(Bool was_humanoid)
  If killscorebuffresettime < GameDaysPassed.GetValue()
    killscorebuff = 0
  EndIf
  killscorebuffresettime = GameDaysPassed.GetValue()+RealTimeSecondsToGameTimeDays(45.0)
  If was_humanoid
    KillScore +=Berserk_Config_Killscorebase.GetValue() as int+killscorebuff
    killscorebuff+=Berserk_Config_Killscoremodifier.GetValue() as int
  EndIf
EndFunction


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Player update Health while Control is Lost
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Bool singlerun_updating_health=False ;just to block updatespam, required If placed in an OnHit
Function update_player_health() ;So the player can monitor his body's health.
  ObjectReference PlayerRef = Game.GetPlayer()
  If singlerun_updating_health
    return
  EndIf
  singlerun_updating_health=True
  mutex_control_status_wait()
  If player_has_control ;just ignore it, the player has the controls
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  Float whealth= BerserkerRef.GetActorValue("health")
  Float phealth= PlayerRef.GetActorValue("health")
  Float diff = phealth-whealth
  If diff > 0
    PlayerRef.damageactorvalue("health",diff)
  ElseIf diff < 0
    PlayerRef.restoreactorvalue("health",-diff)
  EndIf
  mutex_control_status_signal() ;mutex.signal()
  singlerun_updating_health=False
EndFunction


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Player update Position while Control is Lost
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Bool singlerun_updating_pos=False ;just to block updatespam.
Function update_player_pos ();So the player can monitor his body even If it's running away. :)
  ObjectReference PlayerRef = Game.GetPlayer()
  If singlerun_updating_pos
    return
  EndIf
  singlerun_updating_pos=True
  mutex_control_status_wait()
  If player_has_control ;just ignore it, the player has the controls
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  If PlayerRef.GetWorldSpace() == BerserkerRef.GetWorldSpace() && PlayerRef.GetWorldSpace() != None && PlayerRef.GetDistance(BerserkerRef) < 10000 ;Cell.size == 4096 < 10000/sqrt(2) so cell would differ, too.
    ;we are in the same worldspace wuhu, using translate to.
    If BerserkerRef.GetParentCell() == None || BerserkerRef.GetParentCell() != PlayerRef.GetParentCell() || PlayerRef.GetDistance(BerserkerRef) > 500 ;Berserker leftloaded space or got to far away from the Player, moving Player.
      PlayerRef.TranslateTo(BerserkerRef.X, BerserkerRef.Y, BerserkerRef.Z + 3300, BerserkerRef.GetAngleX(), BerserkerRef.GetAngleY(), BerserkerRef.GetAngleZ(), 2000, 0);speed shouldn't be too big or Skyrim could crash because of loading to much Cells in short time, 2000 was ok for Flyable Dragon Races 3 so I will use it.
      PlatRef.SetPosition(BerserkerRef.X, BerserkerRef.Y, BerserkerRef.Z + 3000)
      PlatRef.SetAngle(0, 0, 0)
    EndIf
  Else
    If PlayerRef.GetWorldSpace() != None || BerserkerRef.GetParentCell() == None || BerserkerRef.GetParentCell() != PlayerRef.GetParentCell() ;Berserker left actual cell, moving Player.
      ;either we are inside an iterior cell or in diffent worldspaces, using moveto
      PlayerRef.stoptranslation();just to be sure
      PlayerRef.moveto(BerserkerRef)
      PlatRef.disable()
      PlatRef.delete()
      PlatRef = PlayerRef.PlaceAtMe(DwePlatMid01, 1, True, True)
      PlatRef.SetPosition(PlayerRef.X, PlayerRef.Y, PlayerRef.Z + 3000)
      PlatRef.SetAngle(0, 0, 0)
      PlatRef.Enable()
      PlayerRef.TranslateTo(PlayerRef.X, PlayerRef.Y, PlayerRef.Z + 3300, PlayerRef.GetAngleX(), PlayerRef.GetAngleY(), PlayerRef.GetAngleZ(), 1000, 0)
    EndIf
  EndIf
  
  
  mutex_control_status_signal() ;mutex.signal()
  singlerun_updating_pos=False
EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Initialise LCsystem until Force_control_to_player() is called
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Initialise()
  ;If (Berserk_Config_LooseControlMoral.GetValue() as int == 1);currently checked in mainloop
  ;  return
  ;EndIf
  KillScore=0
  LC_system_active = True
  ;setting up some time untill, the loose controll check runs first
  RegisterForSingleUpdateGameTime(utility.randomfloat(0.25,0.5));run first check after a quater to a half ighour
EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Force Control to Player
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Force_control_to_player()
  ObjectReference PlayerRef = Game.GetPlayer()
  player_is_loosing_control=False
  LC_system_active = False
  UnregisterForUpdateGameTime()
  player_is_loosing_control=False
  PlayerRef.dispelspell(Berserk_Spell_Warningspell)

  ;Remove any used Imagespace Modifier
  
  Gaincontrol()
  KillScore=0
EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function Loose Control Roll: 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function Loose_Control_Roll()
  ObjectReference PlayerRef = Game.GetPlayer()
  ObjectReference Stat = Game.QueryStat(string asStat)
  Float MoralityMeter				; as a max value for MoralityIncrements, default is 500
  Float MoralityIncrements			; contains points accumulated from all used game statistics 
  Int WerewolfTransforms
  Int NecksBitten
  Int Bribes
  Int Intimidations
  Int TGQuests
  Int DBQuests
  Int DaedricQuests
  Int PeopleKilled
  Int AnimalsKilled
  Int UndeadKilled
  Int DaedraKilled
  Int BunniesKilled
  Int DragonSouls
  Int ShoutsLearned
  Int ShoutsMastered
  Int TotalBounty
  Int ItemsPickpocketed
  Int TimesJailed
  Int JailEscapes
  Int ItemsStolen
  Int Assaults
  Int Murders
  Int HorsesStolen
  Int Tresspasses
  Int BountyMoralMultiplier
  Int KillChance=0;
  Int MoralityMeter = Berserk_Config_Moralitymax.GetValue() as int
  Int RandomChance = Utility.RandomInt(1, 100)
  Int Berserk_loose_control_base_chance = Berserk_Config_LooseControlBaseChance.GetValue() as int
  Float Berserk_moral_chance = (MoralityIncrements.GetValue() as int / MoralityMeter.GetValue() as int) * 100
;---------------------
  MoralityIncrements = WerewolfTransforms.GetValue() as int + NecksBitten.GetValue() as int + Bribes.GetValue() as int + Intimidations.GetValue() as int + TGQuests.GetValue() as int + DBQuests.GetValue() as int + DaedricQuests.GetValue() as int + PeopleKilled.GetValue() as int + AnimalsKilled.GetValue() as int + UndeadKilled.GetValue() as int + DaedraKilled.GetValue() as int + BunniesKilled.GetValue() as int + DragonSouls.GetValue() as int + ShoutsLearned.GetValue() as int + ShoutsMastered.GetValue() as int + TotalBounty.GetValue() as int + ItemsPickpocketed.GetValue() as int + TimesJailed.GetValue() as int + JailEscapes.GetValue() as int + ItemsStolen.GetValue() as int + Assaults.GetValue() as int + Murders.GetValue() as int + HorsesStolen.GetValue() as int + Tresspasses.GetValue() as int
  WerewolfTransforms = (Stat("Werewolf Transformations").GetValue() as int) * Berserk_Moral_Config_GSWerewolf.GetValue() as int
  NecksBitten = (Stat("Necks Bitten").GetValue() as int) * Berserk_Moral_Config_GSNeckBit.GetValue() as int
  Bribes = (Stat("Bribes").GetValue() as int) * Berserk_Moral_Config_GSBribe.GetValue() as int
  Intimidations = (Stat("Intimidations").GetValue() as int) * Berserk_Moral_Config_GSIntimidation.GetValue() as int
  TGQuests = (Stat("Thieves' Guild Quests Completed").GetValue() as int) * Berserk_Moral_Config_GSTGQuest.GetValue() as int
  DBQuests = (Stat("The Dark Brotherhood Quests Completed").GetValue() as int) * Berserk_Moral_Config_GSDBQuest.GetValue() as int
  DaedricQuests = (Stat("Daedric Quests Completed").GetValue() as int) * Berserk_Moral_Config_GSDaedricQuest.GetValue() as int
  PeopleKilled = (Stat("People Killed").GetValue() as int) * Berserk_Moral_Config_GSPeopleKill.GetValue() as int
  AnimalsKilled = (Stat("Animals Killed").GetValue() as int) * Berserk_Moral_Config_GSAnimalKill.GetValue() as int
  UndeadKilled = (Stat("Undead Killed").GetValue() as int) * Berserk_Moral_Config_GSUndeadKill.GetValue() as int
  DaedraKilled = (Stat("Daedra Killed").GetValue() as int) * Berserk_Moral_Config_GSDaedraKill.GetValue() as int
  BunniesKilled = (Stat("Bunnies Slaughtered").GetValue() as int) * Berserk_Moral_Config_GSBunnyKill.GetValue() as int
  DragonSouls = (Stat("Dragon Souls Collected").GetValue() as int) * Berserk_Moral_Config_GSDragonSoul.GetValue() as int
  ShoutsLearned = (Stat("Shouts Learned").GetValue() as int) * Berserk_Moral_Config_GSShoutLearn.GetValue() as int
  ShoutsMastered = (Stat("Shouts Mastered").GetValue() as int) * Berserk_Moral_Config_GSShoutMaster.GetValue() as int
  TotalBounty = (Stat("Total Lifetime Bounty").GetValue() as int) * BountyMoralMultiplier.GetValue() as int
  ItemsPickpocketed = (Stat("Items Pickpocketed").GetValue() as int) * Berserk_Moral_Config_GSItemPickpocket.GetValue() as int
  TimesJailed = (Stat("Times Jailed").GetValue() as int) * Berserk_Moral_Config_GSTimeJail.GetValue() as int
  JailEscapes = (Stat("Jail Escapes").GetValue() as int) * Berserk_Moral_Config_GSJailEscape.GetValue() as int
  ItemsStolen = (Stat("Items Stolen").GetValue() as int) * Berserk_Moral_Config_GSItemSteal.GetValue() as int
  Assaults = (Stat("Assaults").GetValue() as int) * Berserk_Moral_Config_GSAssault.GetValue() as int
  Murders = (Stat("Murders").GetValue() as int) * Berserk_Moral_Config_GSMurder.GetValue() as int
  HorsesStolen = (Stat("Horses Stolen").GetValue() as int) * Berserk_Moral_Config_GSHorseSteal.GetValue() as int
  Tresspasses = (Stat("Trespasses").GetValue() as int) * Berserk_Config_Moral_GSTrespass.GetValue() as int
;---------------------
  If (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage1.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage2.GetValue() as int)
     BountyMoralMultiplier = Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage2.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage3.GetValue() as int)
     BountyMoralMultiplier = 2 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage3.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage4.GetValue() as int)
     BountyMoralMultiplier = 3 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage4.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage5.GetValue() as int)
     BountyMoralMultiplier = 4 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage5.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage6.GetValue() as int)
     BountyMoralMultiplier = 5 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage6.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage7.GetValue() as int)
     BountyMoralMultiplier = 6 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage7.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage8.GetValue() as int)
     BountyMoralMultiplier = 7 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage8.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage9.GetValue() as int)
     BountyMoralMultiplier = 8 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage9.GetValue() as int) && (Stat("Total Lifetime Bounty").GetValue() as int < Berserk_Moral_Config_BountyStage10.GetValue() as int)
     BountyMoralMultiplier = 9 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  ElseIf (Stat("Total Lifetime Bounty").GetValue() as int >= Berserk_Moral_Config_BountyStage10.GetValue() as int)
     BountyMoralMultiplier = 10 * Berserk_Moral_Config_GSTotalBounty.GetValue() as int
  EndIF
;---consider to move these into MCM script to avoid cluttering this script
  If Berserk_Moral_Config_BountyStage1.GetValue() as int > Berserk_Moral_Config_BountyStage2.GetValue() as int
	 Berserk_Moral_Config_BountyStage1.GetValue() as int = Berserk_Moral_Config_BountyStage2.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage2.GetValue() as int > Berserk_Moral_Config_BountyStage3.GetValue() as int
	 Berserk_Moral_Config_BountyStage2.GetValue() as int = Berserk_Moral_Config_BountyStage3.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage3.GetValue() as int > Berserk_Moral_Config_BountyStage4.GetValue() as int
	 Berserk_Moral_Config_BountyStage3.GetValue() as int = Berserk_Moral_Config_BountyStage4.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage4.GetValue() as int > Berserk_Moral_Config_BountyStage5.GetValue() as int
	 Berserk_Moral_Config_BountyStage4.GetValue() as int = Berserk_Moral_Config_BountyStage5.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage5.GetValue() as int > Berserk_Moral_Config_BountyStage6.GetValue() as int
	 Berserk_Moral_Config_BountyStage5.GetValue() as int = Berserk_Moral_Config_BountyStage6.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage6.GetValue() as int > Berserk_Moral_Config_BountyStage7.GetValue() as int
	 Berserk_Moral_Config_BountyStage6.GetValue() as int = Berserk_Moral_Config_BountyStage7.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage7.GetValue() as int > Berserk_Moral_Config_BountyStage8.GetValue() as int
	 Berserk_Moral_Config_BountyStage7.GetValue() as int = Berserk_Moral_Config_BountyStage8.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage8.GetValue() as int > Berserk_Moral_Config_BountyStage9.GetValue() as int
	 Berserk_Moral_Config_BountyStage8.GetValue() as int = Berserk_Moral_Config_BountyStage9.GetValue() as int
  ElseIF Berserk_Moral_Config_BountyStage9.GetValue() as int > Berserk_Moral_Config_BountyStage10.GetValue() as int
	 Berserk_Moral_Config_BountyStage9.GetValue() as int = Berserk_Moral_Config_BountyStage10.GetValue() as int
  EndIf
;---Debugging to check whether Stat object reference is working properly or not
  If Stat("People Killed").GetValue() as int == 5
     Debug.Notification("You killed 5 people already, added to People Killed game stat")
  EndIF
  If Stat("People Killed").GetValue() as int == 10
     Debug.Notification("You killed 10 people already, added to People Killed game stat")
  EndIF
  If Stat("People Killed").GetValue() as int == 15
     Debug.Notification("You killed 15 people already, added to People Killed game stat")
  EndIF
  If Stat("Animals Killed").GetValue() as int == 5
     Debug.Notification("You killed 5 animals already, added to Animals Killed game stat")
  EndIF
  If Stat("Animals Killed").GetValue() as int == 10
     Debug.Notification("You killed 10 animals already, added to Animals Killed game stat")
  EndIF
  If Stat("Animals Killed").GetValue() as int == 15
     Debug.Notification("You killed 15 animals already, added to Animals Killed game stat")
  EndIF
;---End of debugging
  KillChance=(KillScore*100)/Berserk_Config_Killscoremax.GetValue() as int
  If KillChance >= 100
	KillChance = 100
  EndIF
  Int BaseWeight=Berserk_Config_LooseControlBaseWeight.GetValue() as int
  Int MoralWeight=Berserk_Config_LooseControlMoralWeight.GetValue() as int
  Int KillWeight=Berserk_Config_LooseControlKillWeight.GetValue() as int
  If BaseWeight == 0 && MoralWeight == 0 && KillWeight == 0 ;we don't want to divide by 0 in the next calculation
    BaseWeight = 1
  EndIf
  Float chance=(BaseWeight*Berserk_loose_control_base_chance + MoralWeight*Berserk_moral_chance + KillWeight*KillChance)/(BaseWeight+MoralWeight+KillWeight)
  If RandomChance<=chance
    player_is_loosing_control=True
    If LC_system_active
      RegisterForSingleUpdateGameTime(utility.randomFloat(RealTimeSecondsToGameTimeHours(30),RealTimeSecondsToGameTimeHours(60)))
    EndIf
    ;apply a short visual warning
    Berserk_Spell_Warningspell.cast(PlayerRef)
  Else
    If LC_system_active
      Float Min = Berserk_Config_LooseControlCooldownTimeMin.GetValue()
      Float Max = Berserk_Config_LooseControlCooldownTimeMax.GetValue()
      If Min >= Max
        Max = Min
      EndIf
      RegisterForSingleUpdateGameTime(utility.randomFloat(RealTimeSecondsToGameTimeHours(Min),RealTimeSecondsToGameTimeHours(Max)))
    EndIf
    KillScore=(KillScore*9)/10
  EndIf
EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function Actually Trigger Control Loss: 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Trigger_control_loss()
  ObjectReference PlayerRef = Game.GetPlayer()
  Int Maxmult = (Berserk_Config_Killscoretimemultmax.GetValue() * 100.0) as Int
  Int KillMult = 100 + (KillScore * (Maxmult - 100)) / Berserk_Config_Killscoremax.GetValue() as int
  If KillMult > Maxmult
    KillMult = Maxmult
  EndIf
  PlayerRef.dispelspell(Berserk_Spell_Warningspell)
  Loosecontrol()
  If LC_system_active
    Float Min = Berserk_Config_LooseControlBaseTimeMin.GetValue()
    Float Max = Berserk_Config_LooseControlBaseTimeMax.GetValue()
    If Min >= Max
      Max = Min
    EndIf
    RegisterForSingleUpdateGameTime(((KillMult as Float)*utility.randomFloat(RealTimeSecondsToGameTimeHours(Min),RealTimeSecondsToGameTimeHours(Max)))/100);lowest is 20 ingame minutes highest is 160 ingame minutes, this sounds fair
  EndIf
  KillScore=(KillScore*9)/10
EndFunction
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Player Loose Control
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Loosecontrol()
  ObjectReference PlayerRef = Game.GetPlayer()
  mutex_control_status_wait()
  ;debug.messagebox("loosing Controls")
  If !player_has_control ;we can only loose controls once
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf

  If (PlayerRef.IsSprinting())
    Debug.SendAnimationEvent(PlayerRef, "SprintStop")
  EndIf
  
  ;utility.wait(5);waits not necessarary, as we don't run it directly while transforming, like the original mod
  Game.ForceThirdPerson() ;just to be sure
  game.setplayeraidriven() ;I don't want to disable the health bar as disableplayercontrols does.

  ;game.disableplayercontrols(abLooking=False)	
  ;Debug.SetGodMode(True)
  PlayerRef.setghost()
  If (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 1) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 2) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 3) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 4)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_1H_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 5) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 6)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_2H_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 7) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 12)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Ranger_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 1 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 2 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 3 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 4 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 8)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Spellsword_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 9)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Mage_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 1 && PlayerRef.GetEquippedItemType(1) == 0)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Unarmed_F, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 1) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 2) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 3) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 4)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_1H_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 5) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 6)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_2H_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 7) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 12)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Ranger_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 1 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 2 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 3 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 4 && (PlayerRef.GetEquippedItemType(0) == 9 || PlayerRef.GetEquippedItemType(0) == 8)) || (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 8)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Spellsword_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 9)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Mage_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  ElseIf (PlayerRef.GetActorBase().GetSex() == 0 && PlayerRef.GetEquippedItemType(1) == 0)
    BerserkerRef = PlayerRef.PlaceAtMe(Berserk_Player_Loose_Controls_Unarmed_M, 1, abForcePersist=True, abInitiallyDisabled = True) as actor
  EndIf
  BerserkerRef.SetPosition(PlayerRef.X, PlayerRef.Y, PlayerRef.Z)
  BerserkerRef.enable()
  Invisibility_originalvalue = PlayerRef.GetBaseActorValue("Invisibility")
  PlayerRef.SetActorValue("Invisibility",100)
  PlayerRef.SetAlpha(0)
  utility.wait(0.1);wait a small bit so spells can apply to the replacement
  utility.wait(0.1);wait a small bit so spells can apply to the replacement
  copy_player_stats_to_replacement ()
;  copy_player_faction_to_replacement ()
  ;BerserkerRef.addspell(Berserk_Ability_Replacement_Helthreg_buff)
  BerserkerAlias.Clear()
  BerserkerAlias.ForceRefTo(BerserkerRef)		;To attach scripts to the berserker
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(BerserkerRef)
  BerserkerRef.TranslateTo(PlayerRef.X, PLayerRef.Y, PlayerRef.Z, PlayerRef.GetAngleX(), PlayerRef.GetAngleY(), PlayerRef.GetAngleZ(), 300, 0)	;For some reason SetAngle() doesnt work for X angle so I'll use TranslateTo to set this angle
  Game.SetCameraTarget(BerserkerRef) ;MUST be runned on an enabled Object
  Game.ForceFirstPerson() ;toggle to first person. Force the game to update the camera when we..
  utility.wait(0.1)
  Game.ForceThirdPerson() ;force third person
  PlatRef = PlayerRef.PlaceAtMe(DwePlatMid01, 1, True, True)
  PlatRef.SetPosition(PlayerRef.X, PlayerRef.Y, PlayerRef.Z + 3000)
  PlatRef.SetAngle(0, 0, 0)
  PlatRef.Enable()
  PlayerRef.TranslateTo(PlayerRef.X, PlayerRef.Y, PlayerRef.Z + 3300, PlayerRef.GetAngleX(), PlayerRef.GetAngleY(), PlayerRef.GetAngleZ(), 1000, 0)
  player_has_control=False
  RegisterForSingleUpdate(3.0)
  ;utility.wait(5);just don't forget to overwrite still active translations (which is quiet unlikly ) in Gaincontrol()
  ;debug.messagebox("loosing Controls done")
  mutex_control_status_signal() ;mutex.signal()
Endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Player Gain Control
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Gaincontrol()
  ObjectReference PlayerRef = Game.GetPlayer()
  ;atomic as read/write to script's own vars is atomic
  mutex_control_status_wait()
  ;endatomic
  ;debug.messagebox("controlgain is started")
  ;debug.messagebox(player_has_control)
  If player_has_control ;we can only gain controls once
    mutex_control_status_signal() ;mutex.signal()
    return
  EndIf
  
  PlayerRef.stoptranslation();just to be sure
  PlayerRef.moveto(BerserkerRef)
  ;PlayerRef.SetPosition(BerserkerRef.X, BerserkerRef.Y, BerserkerRef.Z)
  ;PlayerRef.TranslateTo(BerserkerRef.X, BerserkerRef.Y, BerserkerRef.Z, BerserkerRef.GetAngleX(), BerserkerRef.GetAngleY(), BerserkerRef.GetAngleZ(), 300, 0)	;For some reason SetAngle() doesnt work for X angle so I'll use TranslateTo to set this angle
  Float whealth= BerserkerRef.GetActorValue("health")
  Float phealth= PlayerRef.GetActorValue("health")
  Float diff = phealth-whealth
  If diff > 0
    PlayerRef.damageactorvalue("health",diff)
  ElseIf diff < 0
    PlayerRef.restoreactorvalue("health",-diff)
  EndIf
  Game.SetCameraTarget(PlayerRef)
  BerserkerRef.DisableNoWait()
  PlayerRef.SetActorValue("Invisibility",Invisibility_originalvalue)
  PlayerRef.SetAlpha(1)
  PlayerRef.setghost(False)
  ;Debug.SetGodMode(False)
  game.setplayeraidriven(False)
  ;game.EnablePlayerControls()
  BerserkerAlias.Clear()		
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)
  BerserkerRef.Delete()
  BerserkerRef=none
  PlatRef.Disable()
  PlatRef.Delete()
  PlatRef=none
  player_has_control=True
  UnRegisterForUpdate()
  ;debug.messagebox("controlgain is done")
  mutex_control_status_signal() ;mutex.signal()
  IllusionBlueMassiveImod.apply()
Endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Copy Player stats to Loose Control replacement
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

function copy_player_stats_to_replacement ()
  ObjectReference PlayerRef = Game.GetPlayer()
  If BerserkerRef == NONE
    return
  EndIf
  Float pval
  Float pcval
  Float wval
  string[] primevalues= new string[3]
  primevalues[0]="Health"
  primevalues[1]="Stamina"
  primevalues[2]="Magicka"
  string valname
  Int i=0
  while i<primevalues.length
    valname=primevalues[i]
    pval=PlayerRef.GetBaseActorValue(valname)
    BerserkerRef.setactorvalue(valname,pval)
    pcval=PlayerRef.GetActorValue(valname)
    PlayerRef.restoreactorvalue(valname,100000);to get the modIfied maxval
    pval=PlayerRef.GetActorValue(valname)
    PlayerRef.damageactorvalue(valname,pval-pcval)
    BerserkerRef.restoreactorvalue(valname,100000)
    wval=BerserkerRef.GetActorValue(valname)
    BerserkerRef.modactorvalue(valname,(pval-wval))
    BerserkerRef.restoreactorvalue(valname,100000)
    wval=BerserkerRef.GetActorValue(valname)
    BerserkerRef.damageactorvalue(valname,wval-pcval)
    i+=1
  endwhile
  string[] secvalues= new string[8]
  secvalues[0]="UnarmedDamage"
  secvalues[1]="DamageResist"
  secvalues[2]="DiseaseResist"
  secvalues[3]="PoisonResist"
  secvalues[4]="FireResist"
  secvalues[5]="ElectricResist"
  secvalues[6]="FrostResist"
  secvalues[7]="MagicResist"
  i=0
  while i<secvalues.length
    valname=secvalues[i]
    pval=PlayerRef.GetActorValue(valname)
    BerserkerRef.forceactorvalue(valname,pval)
    i+=1
  endwhile
endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Copy Player Faction to Loose Control replacement
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;function copy_player_faction_to_replacement ()

;  ObjectReference PlayerRef = Game.GetPlayer()
;  If MTE_Config_WolfAlly.GetValue() as int == 1 && !WerewolfRef.IsInFaction(MT_WolfAllyFaction)
;    WerewolfRef.AddtoFaction(MT_WolfAllyFaction)
;    MT_WolfAllyFaction.SetAlly(WolfFaction)
;  EndIf

;  BerserkerRef.AddItem(Berserk_Armor_Updater,1)
;  utility.wait(0.1)
;  BerserkerRef.EquipItem(Berserk_Armor_Updater, true)
  
;  If PlayerREF.IsInFaction(MT_WerewolfFaction) && MT_Player_TherianthropeType.GetValue() as int == 1 && !WerewolfRef.IsInFaction(MT_WerewolfFaction)
;    WerewolfRef.AddtoFaction(MT_WerewolfFaction)
;    WerewolfRef.AddItem(MT_Item_NPC, MT_Player_WerewolfSkin.GetValue() as int, True)
;    (MT_Quest_SkinFramework as MT_Quest_SkinFrameworkScript).NPCWerewolfSkin(WerewolfRef)
;  EndIf

;  If MTE_Config_WolfAlly.GetValue() as int == 1 && PlayerREF.IsInFaction(MT_WerewolfFaction) && MT_Player_TherianthropeType.GetValue() as int == 1 && !WerewolfRef.IsInFaction(MT_WolfAllyFaction)
;    WerewolfRef.AddtoFaction(MT_WolfAllyFaction)
;    MT_WolfAllyFaction.SetAlly(WolfFaction)
;  EndIf

;  If PlayerREF.IsInFaction(MT_WerebearFaction) && MT_Player_TherianthropeType.GetValue() as int == 2 && !WerewolfRef.IsInFaction(MT_WerebearFaction)
;    WerewolfRef.AddtoFaction(MT_WerebearFaction)
;    WerewolfRef.AddItem(MT_Item_NPC, MT_Player_WerebearSkin.GetValue() as int, True)
;    (MT_Quest_SkinFramework as MT_Quest_SkinFrameworkScript).NPCWerebearSkin(WerewolfRef)
;  EndIf
  
;  If MTE_Config_WolfAlly.GetValue() as int == 1 && PlayerREF.IsInFaction(MT_WerebearFaction) && MT_Player_TherianthropeType.GetValue() as int == 2 && !WerewolfRef.IsInFaction(MT_BearAllyFaction)
;    WerewolfRef.AddtoFaction(MT_BearAllyFaction)
;    MT_BearAllyFaction.SetAlly(BearFaction)
;  EndIf
;EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Update Replacement Skin
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Function Update_Replacement_Skin()
;  mutex_control_status_wait()
;  If player_has_control
;    mutex_control_status_signal()
;    return
;  EndIf
;  BerserkerRef.UnEquipItem(Berserk_Armor_Updater, true)
;  utility.wait(0.1)
;  BerserkerRef.EquipItem(Berserk_Armor_Updater, true)
;  mutex_control_status_signal()
;EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Real Time Seconds To Game Time Hours
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Float Function RealTimeSecondsToGameTimeHours(float realtime)

   Float scaledSeconds = realtime * TimeScale.Value
   Return scaledSeconds / (60 * 60)

EndFunction
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Function: Real Time Seconds To Game Time Days
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Float Function RealTimeSecondsToGameTimeDays(float realtime)

   Float scaledSeconds = realtime * TimeScale.Value
   Return scaledSeconds / (60 * 60 * 24)

EndFunction 

 

 

 

Link to comment
Share on other sites

  • 2 weeks later...

You wrote: "I'll post my WIP script here so that people will get a better idea on what i'm working on"

 

My advice use smaller papyrus projects to understand the basics of this language. Find your own coding style.

Use this website as lexicon: http://www.creationkit.com/index.php?title=Category:Scripting

 

Berserk_Loose_Control_Script

 

Scriptname Berserk_Loose_Control_Script extends Quest Conditional
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/

; qwertypol012 wrote: "I'll post my WIP script here so that people will get a better idea on what i'm working on"
; "still currently trying to make Transform Condition System.
; I'm not sure whether it has to be written into its own script or can be included within Transformation script."

; scripts i'm using are based on Moonlight Tales Essentials Overhauled (MTEO) by ubuntufreakdragon"

; -- PROPERTIES --

  Quest  PROPERTY Berserk_Quest_Morality     auto    ; as a factor for loose control roll, referenced for morality, name can be changed to suit better
  Spell  PROPERTY Berserk_Spell_Warningspell auto    ; similar with MTE, but only take visual effects without sound effects
  Static PROPERTY DwePlatMid01               auto

  ImageSpaceModifier PROPERTY IllusionBlueMassiveImod auto
  ImageSpaceModifier PROPERTY FadeToBlackImod     auto
  ImageSpaceModifier PROPERTY FadeToBlackHoldImod auto
  ImageSpaceModifier PROPERTY FadeToBlackBackImod auto

; vanilla
  GlobalVariable PROPERTY GameDaysPassed auto
  GlobalVariable PROPERTY TimeScale      auto

; Global storage
  GlobalVariable PROPERTY Berserk_Moral_Config_GSWerewolf       auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSNeckBit        auto    ;default 8
  GlobalVariable PROPERTY Berserk_Moral_Config_GSBribe          auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSIntimidation   auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTGQuest        auto    ;default 5
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDBQuest        auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDaedricQuest   auto    ;default 100
  GlobalVariable PROPERTY Berserk_Moral_Config_GSPeopleKill     auto    ;default 2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSAnimalKill     auto    ;default 2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSUndeadKill     auto    ;default -2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDaedraKill     auto    ;default -4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSBunnyKill      auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDragonSoul     auto    ;default -2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSShoutLearn     auto    ;default -4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSShoutMaster    auto    ;default -10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTotalBounty    auto    ;default 80
  GlobalVariable PROPERTY Berserk_Moral_Config_GSItemPickpocket auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTimeJail       auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSJailEscape     auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSItemSteal      auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSAssault        auto    ;default 60
  GlobalVariable PROPERTY Berserk_Moral_Config_GSMurder         auto    ;default 80
  GlobalVariable PROPERTY Berserk_Moral_Config_GSHorseSteal     auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTrespass       auto    ;default 2

; bounty stage
  GlobalVariable PROPERTY Berserk_Config_MoralityMax          auto
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage1   auto        ;default 1000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage2   auto        ;default 2000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage3   auto        ;default 3000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage4   auto        ;default 4000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage5   auto        ;default 5000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage6   auto        ;default 6000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage7   auto        ;default 7000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage8   auto        ;default 8000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage9   auto        ;default 9000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage10  auto        ;default 10000

; loose control values
  GlobalVariable PROPERTY Berserk_Config_LooseControlMoral       auto    ; a bool, which check whether moral transform which lead to lost control is on or off, configurable via MCM
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseChance  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseWeight  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlMoralWeight auto    ; default=1, make sure it's counted correctly in the MCM
  GlobalVariable PROPERTY Berserk_Config_LooseControlKillWeight  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlDeathtype   auto

; loose control timings
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseTimeMin     auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseTimeMax     auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlCooldownTimeMin auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlCooldownTimeMax auto

; kill score
  GlobalVariable PROPERTY Berserk_Config_Killscorebase        auto
  GlobalVariable PROPERTY Berserk_Config_Killscoremodifier    auto
  GlobalVariable PROPERTY Berserk_Config_Killscoremax         auto
  GlobalVariable PROPERTY Berserk_Config_Killscoretimemultmax auto


  ActorBase[] PROPERTY Berserk_Player_Loose_Controls_Array auto            ; array to store actorbases as follow
;; male
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_M    auto    ; 0
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_1H_M         auto    ; 1
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_2H_M         auto    ; 2
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_M     auto    ; 3
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_M       auto    ; 4
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_M auto    ; 5
;
;; female
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_F    auto    ; 6
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_1H_F         auto    ; 7
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_2H_F         auto    ; 8
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_F     auto    ; 9
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_F       auto    ; 10
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_F auto    ; 11


  FormList       PROPERTY Berserk_List_secure_caves auto
  ReferenceAlias PROPERTY BerserkerAlias            auto

  Bool PROPERTY player_has_control        = TRUE  auto Conditional
  Bool PROPERTY player_is_loosing_control = False auto Conditional


  Float killscorebuffresettime     = 0.0
  Float Invisibility_originalvalue = 0.0

  Int killscore     = 0
  Int killscorebuff = 0

  Bool LC_system_active          = False
  Bool singlerun_updating_health = False     ; just to block updatespam, required If placed in an OnHit
  Bool singlerun_updating_pos    = False     ; just to block updatespam.

  Actor BerserkerRef
  ObjectReference PlatRef


;--------------------
FUNCTION Initialise()  ; the LC system until Force_control_to_player() is called
;--------------------
; setting up some time until, the loose control check runs first

;If (Berserk_Config_LooseControlMoral.GetValue() as int == 1)    ; currently checked in mainloop
;    return
;EndIf

    KillScore = 0
    LC_system_active = TRUE
    RegisterForSingleUpdateGameTime( Utility.RandomFloat(0.25, 0.5) )    ; run first check after a quater to a half hour
ENDFUNCTION

;---------------------------------
FUNCTION Force_control_to_player()
;---------------------------------
    UnRegisterForUpdateGameTime()
    LC_system_active = False
    myF_Dispel()

    Gaincontrol()            ; Remove any used Imagespace Modifier
    KillScore = 0
ENDFUNCTION


;--------------------
FUNCTION myF_Dispel()  ; internal helper
;--------------------
    player_is_loosing_control = False
    Game.GetPlayer().DispelSpell(Berserk_Spell_Warningspell)
ENDFUNCTION


; -- EVENTs -- 2

EVENT OnUpdate()
IF ( player_has_control )
    RETURN    ; - STOP -
ENDIF
;---------------------
; control is actually lost
 
    update_player_pos()            ; Update Playerpos to load cells
    Utility.Wait(0.1)            ; to let the new cell load if any

    update_player_health()        ; run after update pos, as unloaded NPC's have well unknown health values.
    RegisterForSingleUpdate(2.9)
ENDEVENT


EVENT OnUpdateGameTime()  ; only running while moraltransformations running the main rolls
IF ( LC_system_active )
ELSE
    RETURN    ; - STOP - /0    system not active
ENDIF
;---------------------
IF (Berserk_Config_LooseControlMoral.GetValueInt() == 1) && (player_has_control) && (!player_is_loosing_control)
    Loose_Control_Roll()
    RETURN    ; - STOP - /1    Loose Control check
ENDIF
;---------------------
IF ( player_is_loosing_control )           ;&& !PlayerRef.HasMagicEffect(Berserk_Effect_MoralTransformation)  ;make sure to only call this once transformation is complete
    myF_Dispel()
    Trigger_control_loss()
    RETURN    ; - STOP - /2    loose control
ENDIF
;---------------------
IF ( player_has_control )
    RETURN    ; - STOP - /3    has control back
ENDIF
;---------------------
    update_player_pos()
    Gaincontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF
ENDEVENT


; -- FUNCTIONs -- (3) + 3 + 2 + 10 + 6

;------------------------------------------------------
Float FUNCTION RealTimeSecondsToGameTimeHours(Float fs)  ; convert "Real Time Seconds" to "Game Time Hours"
;------------------------------------------------------
   float f = (TimeScale.GetValue() * fs) / 3600.0                                ; f = scaledSeconds, fs = realTime
   RETURN f
ENDFUNCTION
 
 
;------------------------------------------
Float FUNCTION RTS_ToGameTimeDays(Float fs)  ; convert "Real Time Seconds" To "Game Time Days"
;------------------------------------------
   float f = RealTimeSecondsToGameTimeHours(fs)                                    ; f = scaledSeconds, fs = realTime
   RETURN (f / 24.0)
ENDFUNCTION


;-------------------------------------------------------------------------------------
FUNCTION myF_RegisterForWaitTime(GlobalVariable gMin, GlobalVariable gMax, Bool bKill)  ; internal helper
;-------------------------------------------------------------------------------------
    float fMin = gMin.GetValue()
    float fMax = gMax.GetValue()
    float f
    
    IF (fMin > fMax)        ; failsafe for random
        f    = fMax
        fMax = fMin
        fMin = f
    ENDIF

     f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(fMin), RealTimeSecondsToGameTimeHours(fMax))

    IF ( bKill )
        int iMax = Berserk_Config_Killscoretimemultmax.GetValueInt() * 100                            ; iMax = Maxmult
        int i  = ((iMax - 100) * KillScore / Berserk_Config_Killscoremax.GetValueInt()) + 100        ; i = KillMult
        IF (i > iMax)
            i = iMax
        ENDIF
        f = (i as Float * f) / 100        ; lowest is 20 ingame minutes highest is 160 ingame minutes, this sounds fair
    ENDIF

    RegisterForSingleUpdateGameTime(f)
ENDFUNCTION


;##################### Skyrim runs each Script only once at a time, internal calls and edits will not break this
;### ThreadControl #############################################################################################
;##################### read/write to script's own vars is atomic

  Int mutex_control_status_turn   = 0
  Int mutex_control_status_number = 0

;--------------------
FUNCTION mutex_Wait()  ; mutex_control_status_wait()
;--------------------
  int i = mutex_control_status_number                                            ; i = local_number
  mutex_control_status_number+= 1            ; increased again and again !!!

  WHILE (i != mutex_control_status_turn)
    Utility.Wait(0.1)
  ENDWHILE
ENDFUNCTION

;-------------------
FUNCTION mutex_Add()  ; mutex_control_status_signal()
;-------------------
  mutex_control_status_turn+= 1                ; increased again and again !!!
ENDFUNCTION


;##############
;### Player #############################################################################################
;############## 10

;-----------------------------------------
FUNCTION player_has_killed(Bool bHumanoid)  ; has killed on something
;-----------------------------------------
    float f = GameDaysPassed.GetValue()

    IF (killscorebuffResetTime < f)
        killscorebuff = 0
    ENDIF

    killscorebuffResetTime = f + RTS_ToGameTimeDays(45.0)

    IF ( bHumanoid )                                                            ; bHumanoid =  was_humanoid
        KillScore    += Berserk_Config_Killscorebase.GetValueInt() + killscorebuff
        killscorebuff+= Berserk_Config_Killscoremodifier.GetValueInt()
    ENDIF
ENDFUNCTION


;---------------------
FUNCTION Gaincontrol()  ; Player Gain Control
;---------------------
;;    debug.messagebox("controlgain is started")
;;    debug.messagebox(player_has_control)

    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    we can only gain controls once
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
;;;    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
;;;    IF (i == 0)
        player.SetGhost(False)
;;;    ENDIF

    myF_AdjustHealth(player, BerserkerRef.GetActorValue("health"))

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)

    mutex_Add()                    ; THREADsafe OFF
; ==
    IllusionBlueMassiveImod.Apply()
ENDFUNCTION


;----------------------------------------------------------------------
FUNCTION player_replacement_was_killed(Actor targetRef, Actor akKiller)  ; without Control
;----------------------------------------------------------------------
;;    debug.messagebox(player_has_control)
;;    debug.messagebox(BerserkerRef)
;;    debug.messagebox(targetRef)            ; the_killed_replacement

IF ( !BerserkerRef )
    RETURN    ; - STOP -    just ignore it, None should not die
ENDIF
;---------------------
IF (targetRef == BerserkerRef)
ELSE
    RETURN    ; - STOP -    just ignore it, its the wrong replacement, maybe an older one not deleted yet,
ENDIF                    ; or something spawned by an external script, at least this should never become True
;---------------------
    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
    IF (i == 0)
        player.SetGhost(False)
        player.Kill(akKiller)
    ENDIF

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)

    mutex_Add()                    ; THREADsafe OFF
; ==
    IF (i == 1)
        wake_up_in_cave(player)
    ENDIF
ENDFUNCTION


;-------------------------------------
FUNCTION wake_up_in_cave(Actor player)
;-------------------------------------
  ;Debug.SendAnimationEvent(player, "BreviMoonlightTalesRestS1")

;Todo some eye candy anim
    FadeToBlackImod.Apply()                                ; >> 1
    Utility.Wait(2.1)
    FadeToBlackImod.PopTo(FadeToBlackHoldImod)            ; -> 2

    myF_MoveToSecurePlace(player)
    myF_AdjustHealth(player, 50.0)    ; heal you up to 50 health

    Utility.Wait(0.5)
    player.SetGhost(False)
    player = None

    FadeToBlackHoldImod.PopTo(FadeToBlackBackImod)        ; -> 3
    Utility.Wait(3.0)

    FadeToBlackImod.Remove()                            ; << 1
    FadeToBlackHoldImod.Remove()                        ; << 2
    FadeToBlackBackImod.Remove()                        ; << 3
ENDFUNCTION


;-------------------------------------------
FUNCTION myF_MoveToSecurePlace(Actor player)  ; internal helper
;-------------------------------------------
    int i = Utility.RandomInt(0, Berserk_List_secure_caves.GetSize() - 1)
    formlist myList = Berserk_List_secure_caves.GetAt(i) as Formlist
    objectReference oRef

    IF ( myList )
        oRef = myList.GetAt(0) as ObjectReference          ; get the Berserkermarker which is the first object in the cavedata
;;    ELSE
;;        Debug.Trace(" Formlist not found! " +self)
    ENDIF

    IF ( oRef )
        player.MoveTo(oRef)
        player.SetAngle(0.0, 0.0, 0.0)
;;    ELSE
;;        Debug.Trace(" BersekerMarker is invalid! " +self)
    ENDIF
ENDFUNCTION


;-----------------------------------------------
FUNCTION myF_AdjustHealth(Actor player, Float f)  ; internal helper
;-----------------------------------------------                
    f = player.GetActorValue("health") - f

IF (f > 0)
    player.DamageActorValue("health", f)
    RETURN    ; - STOP -
ENDIF
;---------------------
IF (f == 0)
    RETURN    ; - STOP -    just in case
ENDIF
;---------------------
    player.RestoreActorValue("health", -f)
ENDFUNCTION


;------------------------------
FUNCTION update_player_health() ; So the player can monitor his body's health.
;------------------------------
IF ( singlerun_updating_health )
    RETURN    ; - STOP -
ENDIF
;---------------------
    singlerun_updating_health = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update health while Control is Lost
    myF_AdjustHealth(Game.GetPlayer(), BerserkerRef.GetActorValue("health"))
    mutex_Add()

    singlerun_updating_health = False
ENDFUNCTION


;---------------------------
FUNCTION update_player_pos()  ; So the player can monitor his body even If it's running away. :)
;---------------------------
IF ( singlerun_updating_pos )
    RETURN    ; - STOP -    
ENDIF
;---------------------
    singlerun_updating_pos = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update Position while Control is Lost
    myF_Update()
    mutex_Add()

    singlerun_updating_pos = False
ENDFUNCTION


;--------------------
FUNCTION myF_Update()  ; internal helper
;--------------------
; Cell.size == 4096 < 10000/sqrt(2) so cell would differ, too.

    actor player  = Game.GetPlayer()
    worldSpace ws = player.GetWorldSpace()
    bool bOK
    cell c

    IF ( BerserkerRef )
        c = BerserkerRef.GetParentCell()
        bOK = (!c) || (c != player.GetParentCell())
    ELSE
        bOK = False
    ENDIF

IF (ws) && (ws == BerserkerRef.GetWorldSpace()) && (player.GetDistance(BerserkerRef as ObjectReference) < 10000)
    IF (bOK) || (player.GetDistance(BerserkerRef) > 500)
        myF_Translate(BerserkerRef, 2000.0, False)
    ENDIF
    RETURN    ; - STOP -    we are in the same worldspace wuhu, using translate to.
ENDIF
;---------------------
IF (ws) || (bOK)
    ; Berserker left actual cell, moving Player.
    ; either we are inside an iterior cell or in diffent worldspaces, using moveto

    player.StopTranslation()        ; just to be sure

    IF ( PlatRef )
        PlatRef.DisableNoWait()
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    player.MoveTo(BerserkerRef)
    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)        ; place a new plate

    myF_Translate(player, 1000.0, TRUE)
    PlatRef.EnableNoWait()
ENDIF
ENDFUNCTION


;---------------------------------------------------------
FUNCTION myF_Translate(Actor aRef, Float fSpeed, Bool bOK)  ; internal helper
;---------------------------------------------------------
; speed shouldn't be too big or Skyrim could crash because of loading to much Cells in short time, 2000 was ok for Flyable Dragon Races 3 so I will use it.
; Berserker has left loaded space or got too far away from the Player, moving Player.

    float fx = aRef.GetPositionX()
    float fy = aRef.GetPositionY()
    float fz = aRef.GetPositionZ() + 3300.0

    float aX = aRef.GetAngleX()
    float aY = aRef.GetAngleY()
    float aZ = aRef.GetAngleZ()

IF ( bOK )
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 1000
ELSE
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 2000
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
ENDIF
ENDFUNCTION


;###############
;### Control #############################################################################################
;############### 6

;----------------------------
FUNCTION Loose_Control_Roll()
;----------------------------
    int[] a = new Int[24]        ; query stats storage

    int i
; https://www.creationkit.com/index.php?title=QueryStat_-_Game

    i =  Game.QueryStat("Werewolf Transformations")
    a[0] = i * Berserk_Moral_Config_GSWerewolf.GetValueInt()         ; int WerewolfTransforms

     i = Game.QueryStat("Necks Bitten")
    a[1] = i * Berserk_Moral_Config_GSNeckBit.GetValueInt()          ; int NecksBitten

     i = Game.QueryStat("Bribes")
    a[2] = i * Berserk_Moral_Config_GSBribe.GetValueInt()            ; int Bribes

     i = Game.QueryStat("Intimidations")
    a[3] = i * Berserk_Moral_Config_GSIntimidation.GetValueInt()     ; int Intimidations

     i = Game.QueryStat("Thieves' Guild Quests Completed")
    a[4] = i * Berserk_Moral_Config_GSTGQuest.GetValueInt()          ; int TGQuests

     i = Game.QueryStat("The Dark Brotherhood Quests Completed")
    a[5] = i * Berserk_Moral_Config_GSDBQuest.GetValueInt()          ; int DBQuests

     i = Game.QueryStat("Daedric Quests Completed")
    a[6] = i * Berserk_Moral_Config_GSDaedricQuest.GetValueInt()     ; int DaedricQuests

     i = Game.QueryStat("Dragon Souls Collected")
    a[7] = i * Berserk_Moral_Config_GSDragonSoul.GetValueInt()       ; int DragonSouls

     i = Game.QueryStat("Shouts Learned")
    a[8] = i * Berserk_Moral_Config_GSShoutLearn.GetValueInt()       ; int ShoutsLearned

     i = Game.QueryStat("Shouts Mastered")
    a[9] = i * Berserk_Moral_Config_GSShoutMaster.GetValueInt()      ; int ShoutsMastered

     i = Game.QueryStat("People Killed")
    a[10] = i * Berserk_Moral_Config_GSPeopleKill.GetValueInt()      ; int PeopleKilled

     i = Game.QueryStat("Animals Killed")
    a[11] = i * Berserk_Moral_Config_GSAnimalKill.GetValueInt()      ; int AnimalsKilled

     i = Game.QueryStat("Undead Killed")
    a[12] = i * Berserk_Moral_Config_GSUndeadKill.GetValueInt()      ; int UndeadKilled

     i = Game.QueryStat("Daedra Killed")
    a[13] = i * Berserk_Moral_Config_GSDaedraKill.GetValueInt()      ; int DaedraKilled

     i = Game.QueryStat("Bunnies Slaughtered")
    a[14] = i * Berserk_Moral_Config_GSBunnyKill.GetValueInt()       ; int BunniesKilled

     i = Game.QueryStat("Items Pickpocketed")
    a[15] = i * Berserk_Moral_Config_GSItemPickpocket.GetValueInt()  ; int ItemsPickpocketed

     i = Game.QueryStat("Times Jailed")
    a[16] = i * Berserk_Moral_Config_GSTimeJail.GetValueInt()        ; int TimesJailed

     i = Game.QueryStat("Jail Escapes")
    a[17] = i * Berserk_Moral_Config_GSJailEscape.GetValueInt()      ; int JailEscapes

     i = Game.QueryStat("Items Stolen")
    a[18] = i * Berserk_Moral_Config_GSItemSteal.GetValueInt()       ; int ItemsStolen

     i = Game.QueryStat("Assaults")
    a[19] = i * Berserk_Moral_Config_GSAssault.GetValueInt()         ; int Assaults

     i = Game.QueryStat("Murders")
    a[20] = i * Berserk_Moral_Config_GSMurder.GetValueInt()          ; int Murders

     i = Game.QueryStat("Horses Stolen")
    a[21] = i * Berserk_Moral_Config_GSHorseSteal.GetValueInt()      ; int HorsesStolen

     i = Game.QueryStat("Trespasses")
    a[22] = i * Berserk_Moral_Config_GSTrespass.GetValueInt()        ; int Tresspasses

;--------
     i = Game.QueryStat("Total Lifetime Bounty")
    float f = Berserk_Moral_Config_GSTotalBounty.GetValue()          ; f = BountyMoralMultiplier

    IF     (i < Berserk_Moral_Config_BountyStage1.GetValueInt())
        f = 1.0        ; no multiplier

    ELSEIF (i < Berserk_Moral_Config_BountyStage2.GetValueInt())
        ; keep it

    ELSEIF (i < Berserk_Moral_Config_BountyStage3.GetValueInt())
        f *= 2

    ELSEIF (i < Berserk_Moral_Config_BountyStage4.GetValueInt())
        f *= 3

    ELSEIF (i < Berserk_Moral_Config_BountyStage5.GetValueInt())
        f *= 4

    ELSEIF (i < Berserk_Moral_Config_BountyStage6.GetValueInt())
        f *= 5

    ELSEIF (i < Berserk_Moral_Config_BountyStage7.GetValueInt())
        f *= 6

    ELSEIF (i < Berserk_Moral_Config_BountyStage8.GetValueInt())
        f *= 7

    ELSEIF (i < Berserk_Moral_Config_BountyStage9.GetValueInt())
        f *= 8

    ELSEIF (i < Berserk_Moral_Config_BountyStage10.GetValueInt())
        f *= 9

;;;    ELSEIF (i >= Berserk_Moral_Config_BountyStage10.GetValueInt())
    ELSE
        f *= 10
    ENDIF

    a[23] = i * (f as Int)        ; ### Total Bounty ###

;--------
    float MoralityIncrements    ; contains accumulated points from all used game statistics
    i = 0
    WHILE (i < a.Length)
        MoralityIncrements += a[i] as Float
        i = i + 1
    ENDWHILE

IF myF_NoControl(MoralityIncrements)
    player_is_loosing_control = TRUE

    IF ( LC_system_active )
        f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(30), RealTimeSecondsToGameTimeHours(60))
        RegisterForSingleUpdateGameTime(f)
    ENDIF

    Berserk_Spell_Warningspell.Cast(Game.GetPlayer())    ; apply a short visual warning
    RETURN    ; - STOP -
ENDIF
;---------------------
    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF

    KillScore = KillScore * 9 / 10        ; see  also Trigger_control_loss()
ENDFUNCTION


;----------------------------------------------------
Bool FUNCTION myF_NoControl(Float MoralityIncrements)  ; internal helper
;----------------------------------------------------
    float r = Utility.RandomInt(1, 100) as Float

;;;    Float chance=(BaseWeight*Berserk_loose_control_base_chance + MoralWeight*Berserk_moral_chance + KillWeight*KillChance)/(BaseWeight+MoralWeight+KillWeight)
;    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    int iB = Berserk_Config_LooseControlBaseWeight.GetValueInt()            ; iB = BaseWeight
    int iM = Berserk_Config_LooseControlMoralWeight.GetValueInt()           ; iM = MoralWeight
    int iK = Berserk_Config_LooseControlKillWeight.GetValueInt()            ; iK = KillWeight

    IF (iB == 0) && (iM == 0) && (iK == 0)    
        iB = 1                            ; **
    ENDIF

    float f = (iB as Float) * Berserk_Config_LooseControlBaseChance.GetValue()
;--------
    float MoralityMeter = Berserk_Config_Moralitymax.GetValue()               ; as a max value for MoralityIncrements, default is 500
    f = f + (iM as Float) * MoralityIncrements / MoralityMeter * 100.0        ; Berserk_moral_chance
;--------
    int KillChance = (KillScore * 100) / Berserk_Config_Killscoremax.GetValueInt()
    IF (KillChance >= 100)
        KillChance = 100
    ENDIF
    f = f + (iK * KillChance) as Float
;--------
    f = f / (iB + iM + iK) as Float        ; ** we don't want to divide by 0 here
;--------
    RETURN (r <= f)        ; RandomChance <= chance
ENDFUNCTION


;------------------------------
FUNCTION Trigger_control_loss()  ; Actually Trigger Control Loss
;------------------------------
    Loosecontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlBaseTimeMin, Berserk_Config_LooseControlBaseTimeMax, TRUE)
    ENDIF        

    KillScore = KillScore * 9 / 10
ENDFUNCTION


;----------------------
FUNCTION Loosecontrol()
;----------------------
;;    debug.messagebox("loosing Controls")
    mutex_Wait()

IF ( !player_has_control )        ; we can only loose controls once
    mutex_Add()
    RETURN    ; - STOP -
ENDIF
;---------------------
    actor player = Game.GetPlayer()

    IF player.IsSprinting()
        Debug.SendAnimationEvent(player, "SprintStop")
    ENDIF
    ;utility.wait(5);waits not necessarary, as we don't run it directly while transforming, like the original mod

    Game.ForceThirdPerson()            ; just to be sure
    Game.SetPlayerAiDriven(TRUE)    ; I don't want to disable the health bar as disableplayercontrols does.
    ;Game.disableplayercontrols(abLooking=False)    
    ;Debug.SetGodMode(True)
    player.SetGhost()

    BerserkerRef = myF_PlaceBerserker(player)        ; ###

    float fx = player.GetPositionX()
    float fy = player.GetPositionY()
    float fz = player.GetPositionZ()

    BerserkerRef.SetPosition(fx, fy, fz)
    BerserkerRef.Enable()

    Invisibility_originalvalue = player.GetBaseActorValue("Invisibility")
    player.SetActorValue("Invisibility", 100)
    player.SetAlpha(0.0)
    Utility.Wait(0.2)        ; wait a small bit so spells can apply to the replacement

    copy_player_stats_to_replacement()                ; ###

    BerserkerAlias.Clear()
    BerserkerAlias.ForceRefTo(BerserkerRef)        ; to attach scripts to the berserker
;    (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(BerserkerRef)

;For some reason SetAngle() doesnt work for X angle so I'll use TranslateTo to set this angle
    float aX = player.GetAngleX()
    float aY = player.GetAngleY()
    float aZ = player.GetAngleZ()

    fx = player.GetPositionX()
    fy = pLayer.GetPositionY()
    fz = player.GetPositionZ()

    BerserkerRef.TranslateTo(fx,fy,fz, aX,aY,aZ, 300.0)    

    Game.SetCameraTarget(BerserkerRef)    ; MUST be runned on an enabled Object
    Game.ForceFirstPerson()             ; toggle to first person. Force the game to update the camera when we..
    Utility.Wait(0.1)
 
    Game.ForceThirdPerson()             ; force third person

    IF ( PlatRef )
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)
    PlatRef.SetPosition(fx, fy, fz + 3000.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    PlatRef.Enable()

    aX = player.GetAngleX()
    aY = player.GetAngleY()
    aZ = player.GetAngleZ()

    player.TranslateTo(fx, fy, fz + 3300.0, aX, aY, aZ, 1000.0, 0.0)

    player_has_control = False
    RegisterForSingleUpdate(3.0)

;    utility.wait(5.0)    ; just don't forget to overwrite still active translations (which is quiet unlikly ) in Gaincontrol()
;;    debug.messagebox("loosing Controls done")

    mutex_Add()
ENDFUNCTION


;;----------------------------------------------
;Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (non array version)
;;----------------------------------------------
;    bool bFemale = (player.GetActorBase().GetSex() == 1)
;    int        i = player.GetEquippedItemType(1)            ; right hand
;    actorBase AB
;
;IF ( bFemale )
;    IF     (i == 0)
;                                    AB = Berserk_Player_Loose_Controls_Unarmed_F        ; 0
;    ELSEIF (i == 5) || (i == 6)
;                                    AB = Berserk_Player_Loose_Controls_2H_F             ; 5, 6
;    ELSEIF (i == 8)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ; 8  staff
;    ELSEIF (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Mage_F           ; 9  spell
;    ELSEIF (i == 7) || (i == 12)
;                                    AB = Berserk_Player_Loose_Controls_Ranger_F         ; 7, 12
;    ELSEIF (i >= 1) && (i <= 4)
;        i = player.GetEquippedItemType(0)                    ; left hand
;        IF (i == 8) || (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ;
;        ELSE
;                                    AB = Berserk_Player_Loose_Controls_1H_F             ; 1, 2, 3, 4
;        ENDIF
;    ENDIF
;ELSE
;    IF     (i == 0)
;                                    AB = Berserk_Player_Loose_Controls_Unarmed_M
;    ELSEIF (i == 5) || (i == 6)
;                                    AB = Berserk_Player_Loose_Controls_2H_M
;    ELSEIF (i == 8)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
;    ELSEIF (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Mage_M
;    ELSEIF (i == 7) || (i == 12)
;                                    AB = Berserk_Player_Loose_Controls_Ranger_M
;    ELSEIF (i >= 1) && (i <= 4)
;        i = player.GetEquippedItemType(0)                    ; left hand
;        IF (i == 8) || (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
;        ELSE
;                                    AB = Berserk_Player_Loose_Controls_1H_M
;        ENDIF
;    ENDIF
;ENDIF
;
;IF ( !AB )
;    RETURN None
;ENDIF
;;---------
;    RETURN player.PlaceAtMe(AB, 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
;ENDFUNCTION


;----------------------------------------------
Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (array version)
;----------------------------------------------
    int i = player.GetEquippedItemType(1)            ; right hand
    int n = -1

    IF     (i == 0)
                                    n = 0        ; Berserk_Player_Loose_Controls_Unarmed_M       0 -- 6  Berserk_Player_Loose_Controls_Unarmed_F
    ELSEIF (i == 5) || (i == 6)
                                    n = 2        ; Berserk_Player_Loose_Controls_2H_M            2 -- 8  Berserk_Player_Loose_Controls_2H_F
    ELSEIF (i == 8)
                                    n = 5        ; Berserk_Player_Loose_Controls_Spellsword_M    5 -- 11 Berserk_Player_Loose_Controls_Spellsword_F
    ELSEIF (i == 9)
                                    n = 4        ; Berserk_Player_Loose_Controls_Mage_M          4 -- 10 Berserk_Player_Loose_Controls_Mage_F
    ELSEIF (i == 7) || (i == 12)
                                    n = 3        ; Berserk_Player_Loose_Controls_Ranger_M        3 -- 9  Berserk_Player_Loose_Controls_Ranger_F
    ELSEIF (i >= 1) && (i <= 4)
        i = player.GetEquippedItemType(0)            ; left hand
        IF (i == 8) || (i == 9)
                                    n = 5        ; Berserk_Player_Loose_Controls_Spellsword_M    5 -- 11 Berserk_Player_Loose_Controls_Spellsword_F
        ELSE
                                    n = 1        ; Berserk_Player_Loose_Controls_1H_M            1 -- 7  Berserk_Player_Loose_Controls_1H_F
        ENDIF
    ENDIF

IF (n < 0)
    RETURN None        ; What is about Torch?
ENDIF
;---------
    IF (player.GetActorBase().GetSex() == 1)
        n = n + 6            ; female actorbase position
    ENDIF

    RETURN player.PlaceAtMe(Berserk_Player_Loose_Controls_Array[n], 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
ENDFUNCTION


;------------------------------------------
FUNCTION copy_player_stats_to_replacement()  ; internal helper, copy Player stats to Loose Control replacement
;------------------------------------------
IF ( !BerserkerRef )
    RETURN    ; - STOP -
ENDIF
;---------------------
string[] a = new string[11]                ; a = primevalues + secvalues
    a[0] = "Health"
    a[1] = "Stamina"
    a[2] = "Magicka"
    a[3] = "UnarmedDamage"
    a[4] = "DamageResist"
    a[5] = "DiseaseResist"
    a[6] = "PoisonResist"
    a[7] = "FireResist"
    a[8] = "ElectricResist"
    a[9] = "FrostResist"
    a[10]= "MagicResist"

    actor player = Game.GetPlayer()
    float f                                ; f = pval

int i = 0
    WHILE (i < 3)
        f = player.GetBaseActorValue(a[i])            ; get player base actor value
        BerserkerRef.SetActorValue(a[i], f)

        float fc = player.GetActorValue(a[i])
        player.RestoreActorValue(a[i], 100000)        ; to get the modified maxValue

        f = player.GetActorValue(a[i])                ; get current player max actor value
        player.DamageActorValue(a[i], f - fc)
        BerserkerRef.RestoreActorValue(a[i], 100000)
;        ------------
        float fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.ModActorValue(a[i], f - fw)
        BerserkerRef.RestoreActorValue(a[i], 100000)

        fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.DamageActorValue(a[i], fw - fc)

        i = i + 1
    ENDWHILE

    WHILE (i < a.Length)
        f = player.GetActorValue(a[i])               ; get current player actor value
        BerserkerRef.ForceActorValue(a[i], f)        ; force that to the berseker
        i = i + 1
    ENDWHILE
ENDFUNCTION

 

 

Link to comment
Share on other sites

You wrote: "I'll post my WIP script here so that people will get a better idea on what i'm working on"

 

My advice use smaller papyrus projects to understand the basics of this language. Find your own coding style.

Use this website as lexicon: http://www.creationkit.com/index.php?title=Category:Scripting

 

Berserk_Loose_Control_Script

 

Scriptname Berserk_Loose_Control_Script extends Quest Conditional
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/

; qwertypol012 wrote: "I'll post my WIP script here so that people will get a better idea on what i'm working on"
; "still currently trying to make Transform Condition System.
; I'm not sure whether it has to be written into its own script or can be included within Transformation script."

; scripts i'm using are based on Moonlight Tales Essentials Overhauled (MTEO) by ubuntufreakdragon"

; -- PROPERTIES --

  Quest  PROPERTY Berserk_Quest_Morality     auto    ; as a factor for loose control roll, referenced for morality, name can be changed to suit better
  Spell  PROPERTY Berserk_Spell_Warningspell auto    ; similar with MTE, but only take visual effects without sound effects
  Static PROPERTY DwePlatMid01               auto

  ImageSpaceModifier PROPERTY IllusionBlueMassiveImod auto
  ImageSpaceModifier PROPERTY FadeToBlackImod     auto
  ImageSpaceModifier PROPERTY FadeToBlackHoldImod auto
  ImageSpaceModifier PROPERTY FadeToBlackBackImod auto

; vanilla
  GlobalVariable PROPERTY GameDaysPassed auto
  GlobalVariable PROPERTY TimeScale      auto

; Global storage
  GlobalVariable PROPERTY Berserk_Moral_Config_GSWerewolf       auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSNeckBit        auto    ;default 8
  GlobalVariable PROPERTY Berserk_Moral_Config_GSBribe          auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSIntimidation   auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTGQuest        auto    ;default 5
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDBQuest        auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDaedricQuest   auto    ;default 100
  GlobalVariable PROPERTY Berserk_Moral_Config_GSPeopleKill     auto    ;default 2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSAnimalKill     auto    ;default 2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSUndeadKill     auto    ;default -2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDaedraKill     auto    ;default -4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSBunnyKill      auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSDragonSoul     auto    ;default -2
  GlobalVariable PROPERTY Berserk_Moral_Config_GSShoutLearn     auto    ;default -4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSShoutMaster    auto    ;default -10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTotalBounty    auto    ;default 80
  GlobalVariable PROPERTY Berserk_Moral_Config_GSItemPickpocket auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTimeJail       auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSJailEscape     auto    ;default 10
  GlobalVariable PROPERTY Berserk_Moral_Config_GSItemSteal      auto    ;default 1
  GlobalVariable PROPERTY Berserk_Moral_Config_GSAssault        auto    ;default 60
  GlobalVariable PROPERTY Berserk_Moral_Config_GSMurder         auto    ;default 80
  GlobalVariable PROPERTY Berserk_Moral_Config_GSHorseSteal     auto    ;default 4
  GlobalVariable PROPERTY Berserk_Moral_Config_GSTrespass       auto    ;default 2

; bounty stage
  GlobalVariable PROPERTY Berserk_Config_MoralityMax          auto
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage1   auto        ;default 1000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage2   auto        ;default 2000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage3   auto        ;default 3000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage4   auto        ;default 4000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage5   auto        ;default 5000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage6   auto        ;default 6000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage7   auto        ;default 7000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage8   auto        ;default 8000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage9   auto        ;default 9000
  GlobalVariable PROPERTY Berserk_Moral_Config_BountyStage10  auto        ;default 10000

; loose control values
  GlobalVariable PROPERTY Berserk_Config_LooseControlMoral       auto    ; a bool, which check whether moral transform which lead to lost control is on or off, configurable via MCM
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseChance  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseWeight  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlMoralWeight auto    ; default=1, make sure it's counted correctly in the MCM
  GlobalVariable PROPERTY Berserk_Config_LooseControlKillWeight  auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlDeathtype   auto

; loose control timings
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseTimeMin     auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlBaseTimeMax     auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlCooldownTimeMin auto
  GlobalVariable PROPERTY Berserk_Config_LooseControlCooldownTimeMax auto

; kill score
  GlobalVariable PROPERTY Berserk_Config_Killscorebase        auto
  GlobalVariable PROPERTY Berserk_Config_Killscoremodifier    auto
  GlobalVariable PROPERTY Berserk_Config_Killscoremax         auto
  GlobalVariable PROPERTY Berserk_Config_Killscoretimemultmax auto


  ActorBase[] PROPERTY Berserk_Player_Loose_Controls_Array auto            ; array to store actorbases as follow
;; male
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_M    auto    ; 0
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_1H_M         auto    ; 1
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_2H_M         auto    ; 2
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_M     auto    ; 3
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_M       auto    ; 4
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_M auto    ; 5
;
;; female
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_F    auto    ; 6
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_1H_F         auto    ; 7
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_2H_F         auto    ; 8
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_F     auto    ; 9
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_F       auto    ; 10
;  ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_F auto    ; 11


  FormList       PROPERTY Berserk_List_secure_caves auto
  ReferenceAlias PROPERTY BerserkerAlias            auto

  Bool PROPERTY player_has_control        = TRUE  auto Conditional
  Bool PROPERTY player_is_loosing_control = False auto Conditional


  Float killscorebuffresettime     = 0.0
  Float Invisibility_originalvalue = 0.0

  Int killscore     = 0
  Int killscorebuff = 0

  Bool LC_system_active          = False
  Bool singlerun_updating_health = False     ; just to block updatespam, required If placed in an OnHit
  Bool singlerun_updating_pos    = False     ; just to block updatespam.

  Actor BerserkerRef
  ObjectReference PlatRef


;--------------------
FUNCTION Initialise()  ; the LC system until Force_control_to_player() is called
;--------------------
; setting up some time until, the loose control check runs first

;If (Berserk_Config_LooseControlMoral.GetValue() as int == 1)    ; currently checked in mainloop
;    return
;EndIf

    KillScore = 0
    LC_system_active = TRUE
    RegisterForSingleUpdateGameTime( Utility.RandomFloat(0.25, 0.5) )    ; run first check after a quater to a half hour
ENDFUNCTION

;---------------------------------
FUNCTION Force_control_to_player()
;---------------------------------
    UnRegisterForUpdateGameTime()
    LC_system_active = False
    myF_Dispel()

    Gaincontrol()            ; Remove any used Imagespace Modifier
    KillScore = 0
ENDFUNCTION


;--------------------
FUNCTION myF_Dispel()  ; internal helper
;--------------------
    player_is_loosing_control = False
    Game.GetPlayer().DispelSpell(Berserk_Spell_Warningspell)
ENDFUNCTION


; -- EVENTs -- 2

EVENT OnUpdate()
IF ( player_has_control )
    RETURN    ; - STOP -
ENDIF
;---------------------
; control is actually lost
 
    update_player_pos()            ; Update Playerpos to load cells
    Utility.Wait(0.1)            ; to let the new cell load if any

    update_player_health()        ; run after update pos, as unloaded NPC's have well unknown health values.
    RegisterForSingleUpdate(2.9)
ENDEVENT


EVENT OnUpdateGameTime()  ; only running while moraltransformations running the main rolls
IF ( LC_system_active )
ELSE
    RETURN    ; - STOP - /0    system not active
ENDIF
;---------------------
IF (Berserk_Config_LooseControlMoral.GetValueInt() == 1) && (player_has_control) && (!player_is_loosing_control)
    Loose_Control_Roll()
    RETURN    ; - STOP - /1    Loose Control check
ENDIF
;---------------------
IF ( player_is_loosing_control )           ;&& !PlayerRef.HasMagicEffect(Berserk_Effect_MoralTransformation)  ;make sure to only call this once transformation is complete
    myF_Dispel()
    Trigger_control_loss()
    RETURN    ; - STOP - /2    loose control
ENDIF
;---------------------
IF ( player_has_control )
    RETURN    ; - STOP - /3    has control back
ENDIF
;---------------------
    update_player_pos()
    Gaincontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF
ENDEVENT


; -- FUNCTIONs -- (3) + 3 + 2 + 10 + 6

;------------------------------------------------------
Float FUNCTION RealTimeSecondsToGameTimeHours(Float fs)  ; convert "Real Time Seconds" to "Game Time Hours"
;------------------------------------------------------
   float f = (TimeScale.GetValue() * fs) / 3600.0                                ; f = scaledSeconds, fs = realTime
   RETURN f
ENDFUNCTION
 
 
;------------------------------------------
Float FUNCTION RTS_ToGameTimeDays(Float fs)  ; convert "Real Time Seconds" To "Game Time Days"
;------------------------------------------
   float f = RealTimeSecondsToGameTimeHours(fs)                                    ; f = scaledSeconds, fs = realTime
   RETURN (f / 24.0)
ENDFUNCTION


;-------------------------------------------------------------------------------------
FUNCTION myF_RegisterForWaitTime(GlobalVariable gMin, GlobalVariable gMax, Bool bKill)  ; internal helper
;-------------------------------------------------------------------------------------
    float fMin = gMin.GetValue()
    float fMax = gMax.GetValue()
    float f
    
    IF (fMin > fMax)        ; failsafe for random
        f    = fMax
        fMax = fMin
        fMin = f
    ENDIF

     f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(fMin), RealTimeSecondsToGameTimeHours(fMax))

    IF ( bKill )
        int iMax = Berserk_Config_Killscoretimemultmax.GetValueInt() * 100                            ; iMax = Maxmult
        int i  = ((iMax - 100) * KillScore / Berserk_Config_Killscoremax.GetValueInt()) + 100        ; i = KillMult
        IF (i > iMax)
            i = iMax
        ENDIF
        f = (i as Float * f) / 100        ; lowest is 20 ingame minutes highest is 160 ingame minutes, this sounds fair
    ENDIF

    RegisterForSingleUpdateGameTime(f)
ENDFUNCTION


;##################### Skyrim runs each Script only once at a time, internal calls and edits will not break this
;### ThreadControl #############################################################################################
;##################### read/write to script's own vars is atomic

  Int mutex_control_status_turn   = 0
  Int mutex_control_status_number = 0

;--------------------
FUNCTION mutex_Wait()  ; mutex_control_status_wait()
;--------------------
  int i = mutex_control_status_number                                            ; i = local_number
  mutex_control_status_number+= 1            ; increased again and again !!!

  WHILE (i != mutex_control_status_turn)
    Utility.Wait(0.1)
  ENDWHILE
ENDFUNCTION

;-------------------
FUNCTION mutex_Add()  ; mutex_control_status_signal()
;-------------------
  mutex_control_status_turn+= 1                ; increased again and again !!!
ENDFUNCTION


;##############
;### Player #############################################################################################
;############## 10

;-----------------------------------------
FUNCTION player_has_killed(Bool bHumanoid)  ; has killed on something
;-----------------------------------------
    float f = GameDaysPassed.GetValue()

    IF (killscorebuffResetTime < f)
        killscorebuff = 0
    ENDIF

    killscorebuffResetTime = f + RTS_ToGameTimeDays(45.0)

    IF ( bHumanoid )                                                            ; bHumanoid =  was_humanoid
        KillScore    += Berserk_Config_Killscorebase.GetValueInt() + killscorebuff
        killscorebuff+= Berserk_Config_Killscoremodifier.GetValueInt()
    ENDIF
ENDFUNCTION


;---------------------
FUNCTION Gaincontrol()  ; Player Gain Control
;---------------------
;;    debug.messagebox("controlgain is started")
;;    debug.messagebox(player_has_control)

    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    we can only gain controls once
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
;;;    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
;;;    IF (i == 0)
        player.SetGhost(False)
;;;    ENDIF

    myF_AdjustHealth(player, BerserkerRef.GetActorValue("health"))

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)

    mutex_Add()                    ; THREADsafe OFF
; ==
    IllusionBlueMassiveImod.Apply()
ENDFUNCTION


;----------------------------------------------------------------------
FUNCTION player_replacement_was_killed(Actor targetRef, Actor akKiller)  ; without Control
;----------------------------------------------------------------------
;;    debug.messagebox(player_has_control)
;;    debug.messagebox(BerserkerRef)
;;    debug.messagebox(targetRef)            ; the_killed_replacement

IF ( !BerserkerRef )
    RETURN    ; - STOP -    just ignore it, None should not die
ENDIF
;---------------------
IF (targetRef == BerserkerRef)
ELSE
    RETURN    ; - STOP -    just ignore it, its the wrong replacement, maybe an older one not deleted yet,
ENDIF                    ; or something spawned by an external script, at least this should never become True
;---------------------
    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
    IF (i == 0)
        player.SetGhost(False)
        player.Kill(akKiller)
    ENDIF

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)

    mutex_Add()                    ; THREADsafe OFF
; ==
    IF (i == 1)
        wake_up_in_cave(player)
    ENDIF
ENDFUNCTION


;-------------------------------------
FUNCTION wake_up_in_cave(Actor player)
;-------------------------------------
  ;Debug.SendAnimationEvent(player, "BreviMoonlightTalesRestS1")

;Todo some eye candy anim
    FadeToBlackImod.Apply()                                ; >> 1
    Utility.Wait(2.1)
    FadeToBlackImod.PopTo(FadeToBlackHoldImod)            ; -> 2

    myF_MoveToSecurePlace(player)
    myF_AdjustHealth(player, 50.0)    ; heal you up to 50 health

    Utility.Wait(0.5)
    player.SetGhost(False)
    player = None

    FadeToBlackHoldImod.PopTo(FadeToBlackBackImod)        ; -> 3
    Utility.Wait(3.0)

    FadeToBlackImod.Remove()                            ; << 1
    FadeToBlackHoldImod.Remove()                        ; << 2
    FadeToBlackBackImod.Remove()                        ; << 3
ENDFUNCTION


;-------------------------------------------
FUNCTION myF_MoveToSecurePlace(Actor player)  ; internal helper
;-------------------------------------------
    int i = Utility.RandomInt(0, Berserk_List_secure_caves.GetSize() - 1)
    formlist myList = Berserk_List_secure_caves.GetAt(i) as Formlist
    objectReference oRef

    IF ( myList )
        oRef = myList.GetAt(0) as ObjectReference          ; get the Berserkermarker which is the first object in the cavedata
;;    ELSE
;;        Debug.Trace(" Formlist not found! " +self)
    ENDIF

    IF ( oRef )
        player.MoveTo(oRef)
        player.SetAngle(0.0, 0.0, 0.0)
;;    ELSE
;;        Debug.Trace(" BersekerMarker is invalid! " +self)
    ENDIF
ENDFUNCTION


;-----------------------------------------------
FUNCTION myF_AdjustHealth(Actor player, Float f)  ; internal helper
;-----------------------------------------------                
    f = player.GetActorValue("health") - f

IF (f > 0)
    player.DamageActorValue("health", f)
    RETURN    ; - STOP -
ENDIF
;---------------------
IF (f == 0)
    RETURN    ; - STOP -    just in case
ENDIF
;---------------------
    player.RestoreActorValue("health", -f)
ENDFUNCTION


;------------------------------
FUNCTION update_player_health() ; So the player can monitor his body's health.
;------------------------------
IF ( singlerun_updating_health )
    RETURN    ; - STOP -
ENDIF
;---------------------
    singlerun_updating_health = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update health while Control is Lost
    myF_AdjustHealth(Game.GetPlayer(), BerserkerRef.GetActorValue("health"))
    mutex_Add()

    singlerun_updating_health = False
ENDFUNCTION


;---------------------------
FUNCTION update_player_pos()  ; So the player can monitor his body even If it's running away. :)
;---------------------------
IF ( singlerun_updating_pos )
    RETURN    ; - STOP -    
ENDIF
;---------------------
    singlerun_updating_pos = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update Position while Control is Lost
    myF_Update()
    mutex_Add()

    singlerun_updating_pos = False
ENDFUNCTION


;--------------------
FUNCTION myF_Update()  ; internal helper
;--------------------
; Cell.size == 4096 < 10000/sqrt(2) so cell would differ, too.

    actor player  = Game.GetPlayer()
    worldSpace ws = player.GetWorldSpace()
    bool bOK
    cell c

    IF ( BerserkerRef )
        c = BerserkerRef.GetParentCell()
        bOK = (!c) || (c != player.GetParentCell())
    ELSE
        bOK = False
    ENDIF

IF (ws) && (ws == BerserkerRef.GetWorldSpace()) && (player.GetDistance(BerserkerRef as ObjectReference) < 10000)
    IF (bOK) || (player.GetDistance(BerserkerRef) > 500)
        myF_Translate(BerserkerRef, 2000.0, False)
    ENDIF
    RETURN    ; - STOP -    we are in the same worldspace wuhu, using translate to.
ENDIF
;---------------------
IF (ws) || (bOK)
    ; Berserker left actual cell, moving Player.
    ; either we are inside an iterior cell or in diffent worldspaces, using moveto

    player.StopTranslation()        ; just to be sure

    IF ( PlatRef )
        PlatRef.DisableNoWait()
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    player.MoveTo(BerserkerRef)
    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)        ; place a new plate

    myF_Translate(player, 1000.0, TRUE)
    PlatRef.EnableNoWait()
ENDIF
ENDFUNCTION


;---------------------------------------------------------
FUNCTION myF_Translate(Actor aRef, Float fSpeed, Bool bOK)  ; internal helper
;---------------------------------------------------------
; speed shouldn't be too big or Skyrim could crash because of loading to much Cells in short time, 2000 was ok for Flyable Dragon Races 3 so I will use it.
; Berserker has left loaded space or got too far away from the Player, moving Player.

    float fx = aRef.GetPositionX()
    float fy = aRef.GetPositionY()
    float fz = aRef.GetPositionZ() + 3300.0

    float aX = aRef.GetAngleX()
    float aY = aRef.GetAngleY()
    float aZ = aRef.GetAngleZ()

IF ( bOK )
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 1000
ELSE
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 2000
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
ENDIF
ENDFUNCTION


;###############
;### Control #############################################################################################
;############### 6

;----------------------------
FUNCTION Loose_Control_Roll()
;----------------------------
    int[] a = new Int[24]        ; query stats storage

    int i
; https://www.creationkit.com/index.php?title=QueryStat_-_Game

    i =  Game.QueryStat("Werewolf Transformations")
    a[0] = i * Berserk_Moral_Config_GSWerewolf.GetValueInt()         ; int WerewolfTransforms

     i = Game.QueryStat("Necks Bitten")
    a[1] = i * Berserk_Moral_Config_GSNeckBit.GetValueInt()          ; int NecksBitten

     i = Game.QueryStat("Bribes")
    a[2] = i * Berserk_Moral_Config_GSBribe.GetValueInt()            ; int Bribes

     i = Game.QueryStat("Intimidations")
    a[3] = i * Berserk_Moral_Config_GSIntimidation.GetValueInt()     ; int Intimidations

     i = Game.QueryStat("Thieves' Guild Quests Completed")
    a[4] = i * Berserk_Moral_Config_GSTGQuest.GetValueInt()          ; int TGQuests

     i = Game.QueryStat("The Dark Brotherhood Quests Completed")
    a[5] = i * Berserk_Moral_Config_GSDBQuest.GetValueInt()          ; int DBQuests

     i = Game.QueryStat("Daedric Quests Completed")
    a[6] = i * Berserk_Moral_Config_GSDaedricQuest.GetValueInt()     ; int DaedricQuests

     i = Game.QueryStat("Dragon Souls Collected")
    a[7] = i * Berserk_Moral_Config_GSDragonSoul.GetValueInt()       ; int DragonSouls

     i = Game.QueryStat("Shouts Learned")
    a[8] = i * Berserk_Moral_Config_GSShoutLearn.GetValueInt()       ; int ShoutsLearned

     i = Game.QueryStat("Shouts Mastered")
    a[9] = i * Berserk_Moral_Config_GSShoutMaster.GetValueInt()      ; int ShoutsMastered

     i = Game.QueryStat("People Killed")
    a[10] = i * Berserk_Moral_Config_GSPeopleKill.GetValueInt()      ; int PeopleKilled

     i = Game.QueryStat("Animals Killed")
    a[11] = i * Berserk_Moral_Config_GSAnimalKill.GetValueInt()      ; int AnimalsKilled

     i = Game.QueryStat("Undead Killed")
    a[12] = i * Berserk_Moral_Config_GSUndeadKill.GetValueInt()      ; int UndeadKilled

     i = Game.QueryStat("Daedra Killed")
    a[13] = i * Berserk_Moral_Config_GSDaedraKill.GetValueInt()      ; int DaedraKilled

     i = Game.QueryStat("Bunnies Slaughtered")
    a[14] = i * Berserk_Moral_Config_GSBunnyKill.GetValueInt()       ; int BunniesKilled

     i = Game.QueryStat("Items Pickpocketed")
    a[15] = i * Berserk_Moral_Config_GSItemPickpocket.GetValueInt()  ; int ItemsPickpocketed

     i = Game.QueryStat("Times Jailed")
    a[16] = i * Berserk_Moral_Config_GSTimeJail.GetValueInt()        ; int TimesJailed

     i = Game.QueryStat("Jail Escapes")
    a[17] = i * Berserk_Moral_Config_GSJailEscape.GetValueInt()      ; int JailEscapes

     i = Game.QueryStat("Items Stolen")
    a[18] = i * Berserk_Moral_Config_GSItemSteal.GetValueInt()       ; int ItemsStolen

     i = Game.QueryStat("Assaults")
    a[19] = i * Berserk_Moral_Config_GSAssault.GetValueInt()         ; int Assaults

     i = Game.QueryStat("Murders")
    a[20] = i * Berserk_Moral_Config_GSMurder.GetValueInt()          ; int Murders

     i = Game.QueryStat("Horses Stolen")
    a[21] = i * Berserk_Moral_Config_GSHorseSteal.GetValueInt()      ; int HorsesStolen

     i = Game.QueryStat("Trespasses")
    a[22] = i * Berserk_Moral_Config_GSTrespass.GetValueInt()        ; int Tresspasses

;--------
     i = Game.QueryStat("Total Lifetime Bounty")
    float f = Berserk_Moral_Config_GSTotalBounty.GetValue()          ; f = BountyMoralMultiplier

    IF     (i < Berserk_Moral_Config_BountyStage1.GetValueInt())
        f = 1.0        ; no multiplier

    ELSEIF (i < Berserk_Moral_Config_BountyStage2.GetValueInt())
        ; keep it

    ELSEIF (i < Berserk_Moral_Config_BountyStage3.GetValueInt())
        f *= 2

    ELSEIF (i < Berserk_Moral_Config_BountyStage4.GetValueInt())
        f *= 3

    ELSEIF (i < Berserk_Moral_Config_BountyStage5.GetValueInt())
        f *= 4

    ELSEIF (i < Berserk_Moral_Config_BountyStage6.GetValueInt())
        f *= 5

    ELSEIF (i < Berserk_Moral_Config_BountyStage7.GetValueInt())
        f *= 6

    ELSEIF (i < Berserk_Moral_Config_BountyStage8.GetValueInt())
        f *= 7

    ELSEIF (i < Berserk_Moral_Config_BountyStage9.GetValueInt())
        f *= 8

    ELSEIF (i < Berserk_Moral_Config_BountyStage10.GetValueInt())
        f *= 9

;;;    ELSEIF (i >= Berserk_Moral_Config_BountyStage10.GetValueInt())
    ELSE
        f *= 10
    ENDIF

    a[23] = i * (f as Int)        ; ### Total Bounty ###

;--------
    float MoralityIncrements    ; contains accumulated points from all used game statistics
    i = 0
    WHILE (i < a.Length)
        MoralityIncrements += a[i] as Float
        i = i + 1
    ENDWHILE

IF myF_NoControl(MoralityIncrements)
    player_is_loosing_control = TRUE

    IF ( LC_system_active )
        f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(30), RealTimeSecondsToGameTimeHours(60))
        RegisterForSingleUpdateGameTime(f)
    ENDIF

    Berserk_Spell_Warningspell.Cast(Game.GetPlayer())    ; apply a short visual warning
    RETURN    ; - STOP -
ENDIF
;---------------------
    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF

    KillScore = KillScore * 9 / 10        ; see  also Trigger_control_loss()
ENDFUNCTION


;----------------------------------------------------
Bool FUNCTION myF_NoControl(Float MoralityIncrements)  ; internal helper
;----------------------------------------------------
    float r = Utility.RandomInt(1, 100) as Float

;;;    Float chance=(BaseWeight*Berserk_loose_control_base_chance + MoralWeight*Berserk_moral_chance + KillWeight*KillChance)/(BaseWeight+MoralWeight+KillWeight)
;    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    int iB = Berserk_Config_LooseControlBaseWeight.GetValueInt()            ; iB = BaseWeight
    int iM = Berserk_Config_LooseControlMoralWeight.GetValueInt()           ; iM = MoralWeight
    int iK = Berserk_Config_LooseControlKillWeight.GetValueInt()            ; iK = KillWeight

    IF (iB == 0) && (iM == 0) && (iK == 0)    
        iB = 1                            ; **
    ENDIF

    float f = (iB as Float) * Berserk_Config_LooseControlBaseChance.GetValue()
;--------
    float MoralityMeter = Berserk_Config_Moralitymax.GetValue()               ; as a max value for MoralityIncrements, default is 500
    f = f + (iM as Float) * MoralityIncrements / MoralityMeter * 100.0        ; Berserk_moral_chance
;--------
    int KillChance = (KillScore * 100) / Berserk_Config_Killscoremax.GetValueInt()
    IF (KillChance >= 100)
        KillChance = 100
    ENDIF
    f = f + (iK * KillChance) as Float
;--------
    f = f / (iB + iM + iK) as Float        ; ** we don't want to divide by 0 here
;--------
    RETURN (r <= f)        ; RandomChance <= chance
ENDFUNCTION


;------------------------------
FUNCTION Trigger_control_loss()  ; Actually Trigger Control Loss
;------------------------------
    Loosecontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlBaseTimeMin, Berserk_Config_LooseControlBaseTimeMax, TRUE)
    ENDIF        

    KillScore = KillScore * 9 / 10
ENDFUNCTION


;----------------------
FUNCTION Loosecontrol()
;----------------------
;;    debug.messagebox("loosing Controls")
    mutex_Wait()

IF ( !player_has_control )        ; we can only loose controls once
    mutex_Add()
    RETURN    ; - STOP -
ENDIF
;---------------------
    actor player = Game.GetPlayer()

    IF player.IsSprinting()
        Debug.SendAnimationEvent(player, "SprintStop")
    ENDIF
    ;utility.wait(5);waits not necessarary, as we don't run it directly while transforming, like the original mod

    Game.ForceThirdPerson()            ; just to be sure
    Game.SetPlayerAiDriven(TRUE)    ; I don't want to disable the health bar as disableplayercontrols does.
    ;Game.disableplayercontrols(abLooking=False)    
    ;Debug.SetGodMode(True)
    player.SetGhost()

    BerserkerRef = myF_PlaceBerserker(player)        ; ###

    float fx = player.GetPositionX()
    float fy = player.GetPositionY()
    float fz = player.GetPositionZ()

    BerserkerRef.SetPosition(fx, fy, fz)
    BerserkerRef.Enable()

    Invisibility_originalvalue = player.GetBaseActorValue("Invisibility")
    player.SetActorValue("Invisibility", 100)
    player.SetAlpha(0.0)
    Utility.Wait(0.2)        ; wait a small bit so spells can apply to the replacement

    copy_player_stats_to_replacement()                ; ###

    BerserkerAlias.Clear()
    BerserkerAlias.ForceRefTo(BerserkerRef)        ; to attach scripts to the berserker
;    (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(BerserkerRef)

;For some reason SetAngle() doesnt work for X angle so I'll use TranslateTo to set this angle
    float aX = player.GetAngleX()
    float aY = player.GetAngleY()
    float aZ = player.GetAngleZ()

    fx = player.GetPositionX()
    fy = pLayer.GetPositionY()
    fz = player.GetPositionZ()

    BerserkerRef.TranslateTo(fx,fy,fz, aX,aY,aZ, 300.0)    

    Game.SetCameraTarget(BerserkerRef)    ; MUST be runned on an enabled Object
    Game.ForceFirstPerson()             ; toggle to first person. Force the game to update the camera when we..
    Utility.Wait(0.1)
 
    Game.ForceThirdPerson()             ; force third person

    IF ( PlatRef )
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)
    PlatRef.SetPosition(fx, fy, fz + 3000.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    PlatRef.Enable()

    aX = player.GetAngleX()
    aY = player.GetAngleY()
    aZ = player.GetAngleZ()

    player.TranslateTo(fx, fy, fz + 3300.0, aX, aY, aZ, 1000.0, 0.0)

    player_has_control = False
    RegisterForSingleUpdate(3.0)

;    utility.wait(5.0)    ; just don't forget to overwrite still active translations (which is quiet unlikly ) in Gaincontrol()
;;    debug.messagebox("loosing Controls done")

    mutex_Add()
ENDFUNCTION


;;----------------------------------------------
;Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (non array version)
;;----------------------------------------------
;    bool bFemale = (player.GetActorBase().GetSex() == 1)
;    int        i = player.GetEquippedItemType(1)            ; right hand
;    actorBase AB
;
;IF ( bFemale )
;    IF     (i == 0)
;                                    AB = Berserk_Player_Loose_Controls_Unarmed_F        ; 0
;    ELSEIF (i == 5) || (i == 6)
;                                    AB = Berserk_Player_Loose_Controls_2H_F             ; 5, 6
;    ELSEIF (i == 8)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ; 8  staff
;    ELSEIF (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Mage_F           ; 9  spell
;    ELSEIF (i == 7) || (i == 12)
;                                    AB = Berserk_Player_Loose_Controls_Ranger_F         ; 7, 12
;    ELSEIF (i >= 1) && (i <= 4)
;        i = player.GetEquippedItemType(0)                    ; left hand
;        IF (i == 8) || (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ;
;        ELSE
;                                    AB = Berserk_Player_Loose_Controls_1H_F             ; 1, 2, 3, 4
;        ENDIF
;    ENDIF
;ELSE
;    IF     (i == 0)
;                                    AB = Berserk_Player_Loose_Controls_Unarmed_M
;    ELSEIF (i == 5) || (i == 6)
;                                    AB = Berserk_Player_Loose_Controls_2H_M
;    ELSEIF (i == 8)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
;    ELSEIF (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Mage_M
;    ELSEIF (i == 7) || (i == 12)
;                                    AB = Berserk_Player_Loose_Controls_Ranger_M
;    ELSEIF (i >= 1) && (i <= 4)
;        i = player.GetEquippedItemType(0)                    ; left hand
;        IF (i == 8) || (i == 9)
;                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
;        ELSE
;                                    AB = Berserk_Player_Loose_Controls_1H_M
;        ENDIF
;    ENDIF
;ENDIF
;
;IF ( !AB )
;    RETURN None
;ENDIF
;;---------
;    RETURN player.PlaceAtMe(AB, 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
;ENDFUNCTION


;----------------------------------------------
Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (array version)
;----------------------------------------------
    int i = player.GetEquippedItemType(1)            ; right hand
    int n = -1

    IF     (i == 0)
                                    n = 0        ; Berserk_Player_Loose_Controls_Unarmed_M       0 -- 6  Berserk_Player_Loose_Controls_Unarmed_F
    ELSEIF (i == 5) || (i == 6)
                                    n = 2        ; Berserk_Player_Loose_Controls_2H_M            2 -- 8  Berserk_Player_Loose_Controls_2H_F
    ELSEIF (i == 8)
                                    n = 5        ; Berserk_Player_Loose_Controls_Spellsword_M    5 -- 11 Berserk_Player_Loose_Controls_Spellsword_F
    ELSEIF (i == 9)
                                    n = 4        ; Berserk_Player_Loose_Controls_Mage_M          4 -- 10 Berserk_Player_Loose_Controls_Mage_F
    ELSEIF (i == 7) || (i == 12)
                                    n = 3        ; Berserk_Player_Loose_Controls_Ranger_M        3 -- 9  Berserk_Player_Loose_Controls_Ranger_F
    ELSEIF (i >= 1) && (i <= 4)
        i = player.GetEquippedItemType(0)            ; left hand
        IF (i == 8) || (i == 9)
                                    n = 5        ; Berserk_Player_Loose_Controls_Spellsword_M    5 -- 11 Berserk_Player_Loose_Controls_Spellsword_F
        ELSE
                                    n = 1        ; Berserk_Player_Loose_Controls_1H_M            1 -- 7  Berserk_Player_Loose_Controls_1H_F
        ENDIF
    ENDIF

IF (n < 0)
    RETURN None        ; What is about Torch?
ENDIF
;---------
    IF (player.GetActorBase().GetSex() == 1)
        n = n + 6            ; female actorbase position
    ENDIF

    RETURN player.PlaceAtMe(Berserk_Player_Loose_Controls_Array[n], 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
ENDFUNCTION


;------------------------------------------
FUNCTION copy_player_stats_to_replacement()  ; internal helper, copy Player stats to Loose Control replacement
;------------------------------------------
IF ( !BerserkerRef )
    RETURN    ; - STOP -
ENDIF
;---------------------
string[] a = new string[11]                ; a = primevalues + secvalues
    a[0] = "Health"
    a[1] = "Stamina"
    a[2] = "Magicka"
    a[3] = "UnarmedDamage"
    a[4] = "DamageResist"
    a[5] = "DiseaseResist"
    a[6] = "PoisonResist"
    a[7] = "FireResist"
    a[8] = "ElectricResist"
    a[9] = "FrostResist"
    a[10]= "MagicResist"

    actor player = Game.GetPlayer()
    float f                                ; f = pval

int i = 0
    WHILE (i < 3)
        f = player.GetBaseActorValue(a[i])            ; get player base actor value
        BerserkerRef.SetActorValue(a[i], f)

        float fc = player.GetActorValue(a[i])
        player.RestoreActorValue(a[i], 100000)        ; to get the modified maxValue

        f = player.GetActorValue(a[i])                ; get current player max actor value
        player.DamageActorValue(a[i], f - fc)
        BerserkerRef.RestoreActorValue(a[i], 100000)
;        ------------
        float fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.ModActorValue(a[i], f - fw)
        BerserkerRef.RestoreActorValue(a[i], 100000)

        fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.DamageActorValue(a[i], fw - fc)

        i = i + 1
    ENDWHILE

    WHILE (i < a.Length)
        f = player.GetActorValue(a[i])               ; get current player actor value
        BerserkerRef.ForceActorValue(a[i], f)        ; force that to the berseker
        i = i + 1
    ENDWHILE
ENDFUNCTION

 

 

Thanks for coming here too, ReDragon2013! :laugh:

 

Yes, one really shoud to start from smaller projects first before delving further into the depths of papyrus scripting. But unfortunately, the features i realy want to have in my mod requires such kind of advanced codes, so i have no other choices other than get my hands dirty with such complex papyrus scripting. Fortunately, ubuntufreakdragon made a mod which works just exactly what i need, and he gave me permission to use his scripts as long as i credit him. So that helped me a lot with a robust base script for a feature i want in my mod, and i need to edit and tweak it further (also removing unnecessary codes) in order to get the result i really want.

 

The script i posted here was an "old" version since it has been edited and updated further, although it's not finished yet (i'm still in progress on finishing it). That said, thanks for your edited script and i'll try to elaborate it. If i have some problems or questions regarding it, then sorry if i'll bother you again for asking about it (i'll most likely try to post the new version of the script so that you and other people can take a look at it and see if there's anything need to be corrected or improved). Big thanks to you anyway! I'll surely put your name in the credits for the mod once it's released. :smile:

 

Edit: Just did a quick glance on your edited script, and i find that you've added some significant improvements i could never think of. I could learn a thing or two from this. But now i need to take a rest since my body is atching all over, wanting to go to bed earlier. :laugh:

Edited by qwertypol012
Link to comment
Share on other sites

Alright, so i have somehow -more or less- finished the scripts, except MCM script which i'm still working on to finish. Here i'll post the scripts, hopefully you can take a look and check if there's something wrong or need improvement.

 

There are several scripts. Some of them are linked each other, ie. some functions from a script are called by other scripts. I'll post the scripts with the name on it.

 

1. Berserk_Loose_Control_Script.

This is the main script which governs lose control mechanism.

 

 

Scriptname Berserk_Loose_Control_Script extends Quest Conditional
;Based on MTE_Quest_Loose_Control_Script from Moonlight Tales Essentials Overhauled (MTEO) by ubuntufreakdragon, most credits go to him for providing a robust base script for lose control functionality
;Huge credits go to ReDragon2013 (Nexusmods member) for helping me A LOT in editing and opimizing the script! That guy is really amazing :D

; -- PROPERTIES -- 

Perk Property Berserk_NPC_FallDmg Auto
Spell Property BerserkerLostControlAfterEffects Auto 			;spell to trigger debuff effects after lost control effect finished
Spell Property Berserk_Spell_Warningspell Auto					;similar with MTE, but only take visual effects without sound effects, or switch sound effects into something more fitting
Static Property DwePlatMid01 auto

;other script references
Armor Property ArmorBerserkerWBoots2 Auto
Armor Property ArmorBerserkerWCuirass2 Auto
Armor Property ArmorBerserkerGauntlets2 Auto
Armor Property ArmorBerserkWHelmet Auto
GlobalVariable Property MEValueDefiner Auto				;its value defines what MEs are active
Spell Property BerserkerArmorBeastChange Auto
MagicEffect Property BerserkArmorBeastT Auto
MagicEffect Property BerserkArmorBeastTDanger Auto
SCRBerserkArmorB Property BerserkArmorBeastT Auto
SCRBerserkArmorB Property BerserkArmorBeastTDanger Auto
Formlist Property WornAmmoList Auto

ImageSpaceModifier Property IllusionBlueMassiveImod Auto
ImageSpaceModifier Property FadeToBlackImod Auto
ImageSpaceModifier Property FadeToBlackHoldImod Auto
ImageSpaceModifier Property FadeToBlackBackImod Auto

;vanilla
Idle Property BleedOutStart Auto
Idle Property BleedOutStop Auto
GlobalVariable Property GameDaysPassed Auto
GlobalVariable Property TimeScale Auto
Perk Property AllowShoutingPerk Auto

;global storage
GlobalVariable Property Berserk_Moral_Config_GSWerewolf Auto				;default 10
GlobalVariable Property Berserk_Moral_Config_GSNeckBit Auto					;default 8
GlobalVariable Property Berserk_Moral_Config_GSBribe Auto					;default 1 
GlobalVariable Property Berserk_Moral_Config_GSIntimidation Auto			;default 4 
GlobalVariable Property Berserk_Moral_Config_GSTGQuest Auto					;default 5
GlobalVariable Property Berserk_Moral_Config_GSDBQuest Auto					;default 10 
GlobalVariable Property Berserk_Moral_Config_GSDaedricQuest Auto			;default 100 
GlobalVariable Property Berserk_Moral_Config_GSPeopleKill Auto				;default 2
GlobalVariable Property Berserk_Moral_Config_GSAnimalKill Auto				;default 2
GlobalVariable Property Berserk_Moral_Config_GSUndeadKill Auto				;default -2
GlobalVariable Property Berserk_Moral_Config_GSDaedraKill Auto				;default -4
GlobalVariable Property Berserk_Moral_Config_GSBunnyKill Auto				;default 1
GlobalVariable Property Berserk_Moral_Config_GSDragonSoul Auto				;default -2
GlobalVariable Property Berserk_Moral_Config_GSShoutLearn Auto				;default -4
GlobalVariable Property Berserk_Moral_Config_GSShoutMaster Auto				;default -10
GlobalVariable Property Berserk_Moral_Config_GSTotalBounty Auto				;default 80
GlobalVariable Property Berserk_Moral_Config_GSItemPickpocket Auto			;default 1
GlobalVariable Property Berserk_Moral_Config_GSTimeJail Auto				;default 4
GlobalVariable Property Berserk_Moral_Config_GSJailEscape Auto				;default 10
GlobalVariable Property Berserk_Moral_Config_GSItemSteal Auto				;default 1
GlobalVariable Property Berserk_Moral_Config_GSAssault Auto					;default 60
GlobalVariable Property Berserk_Moral_Config_GSMurder Auto					;default 80
GlobalVariable Property Berserk_Moral_Config_GSHorseSteal Auto				;default 4
GlobalVariable Property Berserk_Moral_Config_GSTrespass Auto				;default 2

;bounty stage
GlobalVariable Property Berserk_Moral_Config_BountyStage1 Auto				;default 1000
GlobalVariable Property Berserk_Moral_Config_BountyStage2 Auto				;default 2000
GlobalVariable Property Berserk_Moral_Config_BountyStage3 Auto				;default 3000
GlobalVariable Property Berserk_Moral_Config_BountyStage4 Auto				;default 4000
GlobalVariable Property Berserk_Moral_Config_BountyStage5 Auto				;default 5000
GlobalVariable Property Berserk_Moral_Config_BountyStage6 Auto				;default 6000
GlobalVariable Property Berserk_Moral_Config_BountyStage7 Auto				;default 7000
GlobalVariable Property Berserk_Moral_Config_BountyStage8 Auto				;default 8000
GlobalVariable Property Berserk_Moral_Config_BountyStage9 Auto				;default 9000
GlobalVariable Property Berserk_Moral_Config_BountyStage10 Auto				;default 10000

;loose control values
GlobalVariable Property Berserk_Config_Moral_Warningtime Auto
GlobalVariable Property Berserk_LooseControlMoral_Enable Auto				;on/off toggle for lost control based on moral
GlobalVariable Property Berserk_Config_LooseControlBaseChance Auto 
GlobalVariable Property Berserk_Config_LooseControlBaseWeight Auto
GlobalVariable Property Berserk_Config_LooseControlMoralWeight Auto			;default is 1, make sure it's counted correctly in the MCM
GlobalVariable Property Berserk_Config_LooseControlDeathtype Auto

;loose control timings
GlobalVariable Property Berserk_Config_LooseControlBaseTimeMin Auto
GlobalVariable Property Berserk_Config_LooseControlBaseTimeMax Auto
GlobalVariable Property Berserk_Config_LooseControlCooldownTimeMin Auto
GlobalVariable Property Berserk_Config_LooseControlCooldownTimeMax Auto

;moral score
;GlobalVariable Property Berserk_Config_MoralScorebase Auto					;not used anymore
GlobalVariable Property Berserk_Config_MoralScoremax Auto
GlobalVariable Property Berserk_Config_MoralScoretimemultmax Auto

;moral status
GlobalVariable Property Berserk_Moral_Transformed Auto

;ActorBase[] PROPERTY Berserk_Player_Loose_Controls_Array auto            ; array to store actorbases as follow (not using array for now)
;; male
ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_M    auto    ; 0
ActorBase PROPERTY Berserk_Player_Loose_Controls_Melee_M      auto    ; 1
ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_M     auto    ; 2
ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_M       auto    ; 3
ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_M auto    ; 4
;
;; female
ActorBase PROPERTY Berserk_Player_Loose_Controls_Unarmed_F    auto    ; 5
ActorBase PROPERTY Berserk_Player_Loose_Controls_Melee_F      auto    ; 6
ActorBase PROPERTY Berserk_Player_Loose_Controls_Ranger_F     auto    ; 7
ActorBase PROPERTY Berserk_Player_Loose_Controls_Mage_F       auto    ; 8
ActorBase PROPERTY Berserk_Player_Loose_Controls_Spellsword_F auto    ; 9

FormList Property Berserk_List_secure_caves Auto
ReferenceAlias Property BerserkerAlias auto

Bool Property player_has_control = True auto conditional
Bool Property player_is_loosing_control = False auto conditional


Float Invisibility_originalvalue = 0.0

Int MoralMax				; as a max value for MoralScore, default is 500
Int MoralScore				; contains accumulated points from all used game statistics 

Bool LC_system_active = False
Bool singlerun_updating_health = False     ; just to block updatespam, required If placed in an OnHit
Bool singlerun_updating_pos = False        ; just to block updatespam.

Actor BerserkerRef
ObjectReference PlatRef


;--------------------
FUNCTION Initialise()  ; the LC system until Force_control_to_player() is called
;--------------------
; setting up some time until, the loose control check runs first

;If (Berserk_LooseControlMoral_Enable.GetValueInt() == 1)    ; currently checked in mainloop
;    return
;EndIf

    MoralScore = 0 
    LC_system_active = TRUE
    RegisterForSingleUpdateGameTime( Utility.RandomFloat(0.25, 0.5) )    ; run first check after a quarter to a half hour
ENDFUNCTION

;---------------------------------
FUNCTION Force_control_to_player()
;---------------------------------
    UnRegisterForUpdateGameTime()
    LC_system_active = False
    myF_Dispel()

    Gaincontrol()            ; Remove any used Imagespace Modifier
    MoralScore = 0 
ENDFUNCTION

;--------------------
FUNCTION myF_Dispel()  ; internal helper
;--------------------
    player_is_loosing_control = False
    Game.GetPlayer().DispelSpell(Berserk_Spell_Warningspell)
ENDFUNCTION

;-------------------------
FUNCTION TransformBack()
;-------------------------
   Force_control_to_player()
   ;UnregisterForUpdateGameTime()	; already included in Force_control_to_player()
   Berserk_Moral_Transformed.SetValue(0)
ENDFUNCTION


; -- EVENTs -- 

EVENT OnUpdate()
IF ( player_has_control )
    RETURN    ; - STOP -
ENDIF
;---------------------
; control is actually lost
 
    update_player_pos()            ; Update Playerpos to load cells
    Utility.Wait(0.1)            ; to let the new cell load if any

    update_player_health()        ; run after update pos, as unloaded NPCs have well unknown health values.
    RegisterForSingleUpdate(2.9)
ENDEVENT


EVENT OnUpdateGameTime()  ; only running while lose control transformations controlled by moral counting running the main rolls
actor player = Game.GetPlayer()
IF ( LC_system_active )
    IF ( player.HasSpell(BerserkerArmorBeastChange) ) || ( player.HasMagicEffect(BerserkArmorBeastT) ) || ( player.HasMagicEffect(BerserkArmorBeastTDanger) )
    ELSE
        RETURN    ; - STOP - /0    either 1 of 3 requirements not fulfilled
	ENDIF
ELSE
    RETURN    ; - STOP - /1    system not active
ENDIF
;---------------------
IF (Berserk_LooseControlMoral_Enable.GetValueInt() == 1) && (player_has_control) && (!player_is_loosing_control)
    IF ( player.HasSpell(BerserkerArmorBeastChange) ) || ( player.HasMagicEffect(BerserkArmorBeastT) ) || ( player.HasMagicEffect(BerserkArmorBeastTDanger) )
        Moral_counting()
        RETURN    ; - STOP - /2    Moral counting, applies both in & out of combat
	ELSEIF ( (player.IsInCombat() == True) && player.HasSpell(BerserkerArmorBeastChange) ) || ( (player.IsInCombat() == True) && player.HasMagicEffect(BerserkArmorBeastT) ) || ( (player.IsInCombat() == True) && player.HasMagicEffect(BerserkArmorBeastTDanger) )
	    Moral_counting()
	    Loose_Control_Roll()
	    RETURN    ; - STOP - /3    Moral counting & Loose Control check, loose control roll only applies in combat
    ENDIF
ENDIF
;---------------------
IF ( player_is_loosing_control ) && ( player.IsInCombat() == True )           ;&& !player.HasMagicEffect(Berserk_Effect_MoralTransformation)  ;make sure to only call this once transformation is complete
    IF ( player.HasSpell(BerserkerArmorBeastChange) ) || ( player.HasMagicEffect(BerserkArmorBeastT) ) || ( player.HasMagicEffect(BerserkArmorBeastTDanger) )
	    myF_Dispel()
        Trigger_control_loss()
        RETURN    ; - STOP - /4    loose control
	ELSE
	    RETURN    ; - STOP - /5    
	ENDIF
ENDIF
;---------------------
IF ( player_has_control )
    RETURN    ; - STOP - /6    has control back
ENDIF
;---------------------
    update_player_pos()
	TransformBack()
    ;Gaincontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF
ENDEVENT


; -- FUNCTIONs -- (3) + 3 + 2 + 10 + 6

;------------------------------------------------------
Float FUNCTION RealTimeSecondsToGameTimeHours(Float fs)  ; convert "Real Time Seconds" to "Game Time Hours"
;------------------------------------------------------
   float f = (TimeScale.GetValue() * fs) / 3600.0                                ; f = scaledSeconds, fs = realTime
   RETURN f
ENDFUNCTION
 
 
;------------------------------------------
Float FUNCTION RealTimeSecondsToGameTimeDays(Float fs)  ; convert "Real Time Seconds" To "Game Time Days"
;------------------------------------------
   float f = RealTimeSecondsToGameTimeHours(fs)                                    ; f = scaledSeconds, fs = realTime
   RETURN (f / 24.0)
ENDFUNCTION


;-------------------------------------------------------------------------------------
FUNCTION myF_RegisterForWaitTime(GlobalVariable gMin, GlobalVariable gMax, Bool bMoral)  ; internal helper
;-------------------------------------------------------------------------------------
    float fMin = gMin.GetValue()
    float fMax = gMax.GetValue()
    float f
    
    IF (fMin > fMax)        ; failsafe for random
        f    = fMax
        fMax = fMin
        fMin = f
    ENDIF

     f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(fMin), RealTimeSecondsToGameTimeHours(fMax))

    IF ( bMoral )
        int iMax = Berserk_Config_MoralScoretimemultmax.GetValueInt() * 100                            ; iMax = Maxmult
        int i  = ((iMax - 100) * MoralScore / Berserk_Config_MoralScoremax.GetValueInt()) + 100        ; i = MoralMult
        IF (i > iMax)
            i = iMax
        ENDIF
        f = (i as Float * f) / 100        ; lowest is 20 ingame minutes highest is 160 ingame minutes, this sounds fair
    ENDIF

    RegisterForSingleUpdateGameTime(f)
ENDFUNCTION


;##################### Skyrim runs each Script only once at a time, internal calls and edits will not break this
;### ThreadControl #############################################################################################
;##################### read/write to script's own vars is atomic

  Int mutex_control_status_turn   = 0
  Int mutex_control_status_number = 0

;--------------------
FUNCTION mutex_Wait()  ; mutex_control_status_wait()
;--------------------
  int i = mutex_control_status_number                                            ; i = local_number
  mutex_control_status_number+= 1            ; increased again and again !!!

  WHILE (i != mutex_control_status_turn)
    Utility.Wait(0.1)
  ENDWHILE
ENDFUNCTION

;-------------------
FUNCTION mutex_Add()  ; mutex_control_status_signal()
;-------------------
  mutex_control_status_turn+= 1                ; increased again and again !!!
ENDFUNCTION


;##############
;### Player #############################################################################################
;############## 10

;---------------------
FUNCTION Gaincontrol()  ; Player Gain Control
;---------------------
;;    debug.messagebox("controlgain is started")
;;    debug.messagebox(player_has_control)

    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    we can only gain controls once
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
;;;    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
;;;    IF (i == 0)
        player.SetGhost(False)
;;;    ENDIF

    myF_AdjustHealth(player, BerserkerRef.GetActorValue("health"))

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)  ;leftover code from MTEO for follower system, not needed here

;;    debug.messagebox("controlgain is done")
    mutex_Add()                    ; THREADsafe OFF
; ==
    IllusionBlueMassiveImod.Apply()
	player.PlayIdle(BleedOutStart)
    Utility.Wait(1.5)
    player.PlayIdle(BleedOutStop)
    MEValueDefiner.SetValue(1)		;set the value to activate damage attributes and debuffs
    BerserkerLostControlAfterEffects.Cast(player)
	Initialise()
ENDFUNCTION


;----------------------------------------------------------------------
FUNCTION player_replacement_was_killed(Actor targetRef, Actor akKiller)  ; without Control
;----------------------------------------------------------------------
;;    debug.messagebox(player_has_control)
;;    debug.messagebox(BerserkerRef)
;;    debug.messagebox(targetRef)            ; the_killed_replacement

IF ( !BerserkerRef )
    RETURN    ; - STOP -    just ignore it, None should not die
ENDIF
;---------------------
IF (targetRef == BerserkerRef)
ELSE
    RETURN    ; - STOP -   targetRef != BerserkerRef; just ignore it, its the wrong replacement, maybe an older one not deleted yet,
ENDIF                    ; or something spawned by an external script, at least this should never become True
;---------------------
    mutex_Wait()                ; THREADsafe ON

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;---------------------
    UnRegisterForUpdate()

; ==
    actor player = Game.GetPlayer()
    player.StopTranslation()            ; just to be sure
    player.MoveTo(BerserkerRef)
    player.SetAngle(0.0, 0.0, 0.0)

    ;Debug.SetGodMode(False)
    Game.SetCameraTarget(player)
    player.SetActorValue("Invisibility", Invisibility_originalvalue)
    player.SetAlpha(1.0)

; ==
    PlatRef.DisableNoWait()
    PlatRef.Delete()
    PlatRef = None

; ==
    int i = Berserk_Config_LooseControlDeathtype.GetValueInt()                    ; i = deathtype
    IF (i == 0)
        player.SetGhost(False)
        player.Kill(akKiller)
    ENDIF

; ==
    Game.SetPlayerAiDriven(False)
    player_has_control = TRUE

; ==
    BerserkerAlias.Clear()        
    BerserkerRef.DisableNoWait()
    BerserkerRef.Delete()
    BerserkerRef = None
;  (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(None)  ;leftover code from MTEO for follower system, not needed here

    mutex_Add()                    ; THREADsafe OFF
; ==
    IF (i == 1)
        wake_up_in_cave(player)
    ENDIF
ENDFUNCTION


;-------------------------------------
FUNCTION wake_up_in_cave(Actor player)
;-------------------------------------
  ;Debug.SendAnimationEvent(player, "BreviMoonlightTalesRestS1")

;Todo some eye candy anim
    FadeToBlackImod.Apply()                                ; >> 1
    Utility.Wait(2.1)
    FadeToBlackImod.PopTo(FadeToBlackHoldImod)            ; -> 2

    myF_MoveToSecurePlace(player)
    myF_AdjustHealth(player, 50.0)    ; heal you up to 50 health

    Utility.Wait(0.5)
    player.SetGhost(False)
    player = None

    FadeToBlackHoldImod.PopTo(FadeToBlackBackImod)        ; -> 3
    Utility.Wait(3.0)

    FadeToBlackImod.Remove()                            ; << 1
    FadeToBlackHoldImod.Remove()                        ; << 2
    FadeToBlackBackImod.Remove()                        ; << 3
	MEValueDefiner.SetValue(1)		;set the value to activate damage attributes and debuffs
    BerserkerLostControlAfterEffects.Cast(player)
ENDFUNCTION


;-------------------------------------------
FUNCTION myF_MoveToSecurePlace(Actor player)  ; internal helper
;-------------------------------------------
    int i = Utility.RandomInt(0, Berserk_List_secure_caves.GetSize() - 1)
    formlist myList = Berserk_List_secure_caves.GetAt(i) as Formlist
    objectReference oRef

    IF ( myList )
        oRef = myList.GetAt(0) as ObjectReference          ; get the Berserkermarker which is the first object in the cavedata
;;    ELSE
;;        Debug.Trace(" Formlist not found! " +self)
    ENDIF

    IF ( oRef )
        player.MoveTo(oRef)
        player.SetAngle(0.0, 0.0, 0.0)
;;    ELSE
;;        Debug.Trace(" BersekerMarker is invalid! " +self)
    ENDIF
ENDFUNCTION


;-----------------------------------------------
FUNCTION myF_AdjustHealth(Actor player, Float f)  ; internal helper
;-----------------------------------------------                
    f = player.GetActorValue("health") - f

IF (f > 0)
    player.DamageActorValue("health", f)
    RETURN    ; - STOP -
ENDIF
;---------------------
IF (f == 0)
    RETURN    ; - STOP -    just in case
ENDIF
;---------------------
    player.RestoreActorValue("health", -f)
ENDFUNCTION


;------------------------------
FUNCTION update_player_health() ; So the player can monitor his body's health.
;------------------------------
IF ( singlerun_updating_health )
    RETURN    ; - STOP -
ENDIF
;---------------------
    singlerun_updating_health = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update health while Control is Lost
    myF_AdjustHealth(Game.GetPlayer(), BerserkerRef.GetActorValue("health"))
    mutex_Add()

    singlerun_updating_health = False
ENDFUNCTION


;---------------------------
FUNCTION update_player_pos()  ; So the player can monitor his body even If it's running away. :)
;---------------------------
IF ( singlerun_updating_pos )
    RETURN    ; - STOP -    
ENDIF
;---------------------
    singlerun_updating_pos = TRUE
    mutex_Wait()

IF ( player_has_control )
    mutex_Add()
    RETURN    ; - STOP -    just ignore it, the player has the controls
ENDIF
;--------------------- update Position while Control is Lost
    myF_Update()
    mutex_Add()

    singlerun_updating_pos = False
ENDFUNCTION


;--------------------
FUNCTION myF_Update()  ; internal helper
;--------------------
; Cell.size == 4096 < 10000/sqrt(2) so cell would differ, too.

    actor player  = Game.GetPlayer()
    worldSpace ws = player.GetWorldSpace()
    bool bOK
    cell c

    IF ( BerserkerRef )
        c = BerserkerRef.GetParentCell()
        bOK = (!c) || (c != player.GetParentCell())
    ELSE
        bOK = False
    ENDIF

IF (ws) && (ws == BerserkerRef.GetWorldSpace()) && (player.GetDistance(BerserkerRef as ObjectReference) < 10000)
    IF (bOK) || (player.GetDistance(BerserkerRef) > 500)
        myF_Translate(BerserkerRef, 2000.0, False)
    ENDIF
    RETURN    ; - STOP -    we are in the same worldspace wuhu, using translate to.
ENDIF
;---------------------
IF (ws) || (bOK)
    ; Berserker left actual cell, moving Player.
    ; either we are inside an interior cell or in diffent worldspaces, using moveto

    player.StopTranslation()        ; just to be sure

    IF ( PlatRef )
        PlatRef.DisableNoWait()
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    player.MoveTo(BerserkerRef)
    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)        ; place a new plate

    myF_Translate(player, 1000.0, TRUE)
    PlatRef.EnableNoWait()
ENDIF
ENDFUNCTION


;---------------------------------------------------------
FUNCTION myF_Translate(Actor aRef, Float fSpeed, Bool bOK)  ; internal helper
;---------------------------------------------------------
; speed shouldn't be too big or Skyrim could crash because of loading to much Cells in short time, 2000 was ok for Flyable Dragon Races 3 so I will use it.
; Berserker has left loaded space or got too far away from the Player, moving Player.

    float fx = aRef.GetPositionX()
    float fy = aRef.GetPositionY()
    float fz = aRef.GetPositionZ() + 3300.0

    float aX = aRef.GetAngleX()
    float aY = aRef.GetAngleY()
    float aZ = aRef.GetAngleZ()

IF ( bOK )
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 1000
ELSE
    aRef.TranslateTo(fx,fy,fz, aX,aY,aZ, fSpeed)        ; 2000
    PlatRef.SetPosition(fx, fy, fz - 300.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
ENDIF
ENDFUNCTION


;###############
;### Control #############################################################################################
;############### 6

;----------------------------
FUNCTION Moral_counting()
;----------------------------
    int[] a = new Int[24]        ; query stats storage

    int i
; https://www.creationkit.com/index.php?title=QueryStat_-_Game

    i =  Game.QueryStat("Werewolf Transformations")
    a[0] = i * Berserk_Moral_Config_GSWerewolf.GetValueInt()         ; int WerewolfTransforms

     i = Game.QueryStat("Necks Bitten")
    a[1] = i * Berserk_Moral_Config_GSNeckBit.GetValueInt()          ; int NecksBitten

     i = Game.QueryStat("Bribes")
    a[2] = i * Berserk_Moral_Config_GSBribe.GetValueInt()            ; int Bribes

     i = Game.QueryStat("Intimidations")
    a[3] = i * Berserk_Moral_Config_GSIntimidation.GetValueInt()     ; int Intimidations

     i = Game.QueryStat("Thieves' Guild Quests Completed")
    a[4] = i * Berserk_Moral_Config_GSTGQuest.GetValueInt()          ; int TGQuests

     i = Game.QueryStat("The Dark Brotherhood Quests Completed")
    a[5] = i * Berserk_Moral_Config_GSDBQuest.GetValueInt()          ; int DBQuests

     i = Game.QueryStat("Daedric Quests Completed")
    a[6] = i * Berserk_Moral_Config_GSDaedricQuest.GetValueInt()     ; int DaedricQuests

     i = Game.QueryStat("Dragon Souls Collected")
    a[7] = -i * Berserk_Moral_Config_GSDragonSoul.GetValueInt()       ; int DragonSouls, has minus value

     i = Game.QueryStat("Shouts Learned")
    a[8] = -i * Berserk_Moral_Config_GSShoutLearn.GetValueInt()       ; int ShoutsLearned, has minus value

     i = Game.QueryStat("Shouts Mastered")
    a[9] = -i * Berserk_Moral_Config_GSShoutMaster.GetValueInt()      ; int ShoutsMastered, has minus value

     i = Game.QueryStat("People Killed")
    a[10] = i * Berserk_Moral_Config_GSPeopleKill.GetValueInt()      ; int PeopleKilled

     i = Game.QueryStat("Animals Killed")
    a[11] = i * Berserk_Moral_Config_GSAnimalKill.GetValueInt()      ; int AnimalsKilled

     i = Game.QueryStat("Undead Killed")
    a[12] = -i * Berserk_Moral_Config_GSUndeadKill.GetValueInt()      ; int UndeadKilled, has minus value

     i = Game.QueryStat("Daedra Killed")
    a[13] = -i * Berserk_Moral_Config_GSDaedraKill.GetValueInt()      ; int DaedraKilled, has minus value

     i = Game.QueryStat("Bunnies Slaughtered")
    a[14] = i * Berserk_Moral_Config_GSBunnyKill.GetValueInt()       ; int BunniesKilled

     i = Game.QueryStat("Items Pickpocketed")
    a[15] = i * Berserk_Moral_Config_GSItemPickpocket.GetValueInt()  ; int ItemsPickpocketed

     i = Game.QueryStat("Times Jailed")
    a[16] = i * Berserk_Moral_Config_GSTimeJail.GetValueInt()        ; int TimesJailed

     i = Game.QueryStat("Jail Escapes")
    a[17] = i * Berserk_Moral_Config_GSJailEscape.GetValueInt()      ; int JailEscapes

     i = Game.QueryStat("Items Stolen")
    a[18] = i * Berserk_Moral_Config_GSItemSteal.GetValueInt()       ; int ItemsStolen

     i = Game.QueryStat("Assaults")
    a[19] = i * Berserk_Moral_Config_GSAssault.GetValueInt()         ; int Assaults

     i = Game.QueryStat("Murders")
    a[20] = i * Berserk_Moral_Config_GSMurder.GetValueInt()          ; int Murders

     i = Game.QueryStat("Horses Stolen")
    a[21] = i * Berserk_Moral_Config_GSHorseSteal.GetValueInt()      ; int HorsesStolen

     i = Game.QueryStat("Trespasses")
    a[22] = i * Berserk_Moral_Config_GSTrespass.GetValueInt()        ; int Tresspasses

;--------
     i = Game.QueryStat("Total Lifetime Bounty")
    float f = Berserk_Moral_Config_GSTotalBounty.GetValue()          ; f = BountyMoralMultiplier

    IF     (i < Berserk_Moral_Config_BountyStage1.GetValueInt())
        f = 1.0        ; no multiplier

    ELSEIF (i < Berserk_Moral_Config_BountyStage2.GetValueInt())
        ; keep it

    ELSEIF (i < Berserk_Moral_Config_BountyStage3.GetValueInt())
        f *= 2

    ELSEIF (i < Berserk_Moral_Config_BountyStage4.GetValueInt())
        f *= 3

    ELSEIF (i < Berserk_Moral_Config_BountyStage5.GetValueInt())
        f *= 4

    ELSEIF (i < Berserk_Moral_Config_BountyStage6.GetValueInt())
        f *= 5

    ELSEIF (i < Berserk_Moral_Config_BountyStage7.GetValueInt())
        f *= 6

    ELSEIF (i < Berserk_Moral_Config_BountyStage8.GetValueInt())
        f *= 7

    ELSEIF (i < Berserk_Moral_Config_BountyStage9.GetValueInt())
        f *= 8

    ELSEIF (i < Berserk_Moral_Config_BountyStage10.GetValueInt())
        f *= 9

;;;    ELSEIF (i >= Berserk_Moral_Config_BountyStage10.GetValueInt())
    ELSE
        f *= 10
    ENDIF

    a[23] = i * (f as Int)        ; ### Total Bounty ###

;--------
    float MoralScore    ; contains accumulated points from all used game statistics
    i = 0
    WHILE (i < a.Length)
        MoralScore += a[i] as Float
        i = i + 1
    ENDWHILE
	float MoralMax = Berserk_Config_MoralScoremax.GetValue()        ; as a max value for MoralScore, default is 500
	IF  (MoralScore >= MoralMax)
	     MoralScore = MoralMax
	ENDIF
ENDFUNCTION


;-------------------------------
FUNCTION Loose_Control_Roll()
;-------------------------------
IF myF_NoControl(MoralScore)
    player_is_loosing_control = TRUE

    IF ( LC_system_active )
        f = Utility.RandomFloat(RealTimeSecondsToGameTimeHours(30), RealTimeSecondsToGameTimeHours(60))
        RegisterForSingleUpdateGameTime(f)
    ENDIF

    IF ( Berserk_Config_Moral_Warningtime.GetValueInt() == 1 )
	    Berserk_Spell_Warningspell.Cast(Game.GetPlayer())    ; apply a short visual warning
        RETURN    ; - STOP -
	ENDIF
ENDIF
;---------------------
    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlCooldownTimeMin, Berserk_Config_LooseControlCooldownTimeMax, False)
    ENDIF

;    MoralScore = MoralScore * 9 / 10        ; see  also Trigger_control_loss()
ENDFUNCTION


;----------------------------------------------------
Bool FUNCTION myF_NoControl(Float MoralScore)  ; internal helper
;----------------------------------------------------
    float r = Utility.RandomInt(1, 100) as Float

;;;    Float chance=(BaseWeight*Berserk_loose_control_base_chance + MoralWeight*Berserk_moral_chance)/(BaseWeight+MoralWeight)
;    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    int iB = Berserk_Config_LooseControlBaseWeight.GetValueInt()            ; iB = BaseWeight
    int iM = Berserk_Config_LooseControlMoralWeight.GetValueInt()           ; iM = MoralWeight

    IF (iB == 0) && (iM == 0)  
        iB = 1                            ; **
    ENDIF

    float f = (iB as Float) * Berserk_Config_LooseControlBaseChance.GetValue()
;--------
    float MoralMax = Berserk_Config_MoralScoremax.GetValue()               ; as a max value for MoralScore, default is 500
	float MoralChance = MoralScore / MoralMax * 100.0					   ; Berserk_moral_chance
	IF (MoralChance >= 100)
	   MoralChance = 100
	ENDIF
    f = f + (iM as Float) * MoralChance 
;--------
    f = f / (iB + iM) as Float        ; ** we don't want to divide by 0 here
;--------
    RETURN (r <= f)        ; RandomChance <= chance
ENDFUNCTION


;------------------------------
FUNCTION Trigger_control_loss()  ; Actually Trigger Control Loss
;------------------------------
    Loosecontrol()

    IF ( LC_system_active )
        myF_RegisterForWaitTime(Berserk_Config_LooseControlBaseTimeMin, Berserk_Config_LooseControlBaseTimeMax, TRUE)
    ENDIF        

ENDFUNCTION


;----------------------
FUNCTION Loosecontrol()
;----------------------
;;    debug.messagebox("loosing Controls")
    mutex_Wait()

IF ( !player_has_control )        ; we can only loose controls once
    mutex_Add()
    RETURN    ; - STOP -
ENDIF
;---------------------
    actor player = Game.GetPlayer()

    IF player.IsSprinting()
        Debug.SendAnimationEvent(player, "SprintStop")
    ENDIF
    ;utility.wait(5);waits not necessarary, as we don't run it directly while transforming, like the original mod

    Game.ForceThirdPerson()            ; just to be sure
    Game.SetPlayerAiDriven(TRUE)    ; I don't want to disable the health bar as disableplayercontrols does.
    ;Game.disableplayercontrols(abLooking=False)    
    ;Debug.SetGodMode(True)
    player.SetGhost()

    BerserkerRef = myF_PlaceBerserker(player)        ; ###

    float fx = player.GetPositionX()
    float fy = player.GetPositionY()
    float fz = player.GetPositionZ()

    BerserkerRef.SetPosition(fx, fy, fz)
    BerserkerRef.Enable()

    Invisibility_originalvalue = player.GetBaseActorValue("Invisibility")
    player.SetActorValue("Invisibility", 100)
    player.SetAlpha(0.0)
    Utility.Wait(0.2)        ; wait a small bit so spells can apply to the replacement
	IF player.HasMagicEffect(BerserkArmorBeastT)
	   (BerserkArmorBeastT as SCRBerserkArmorB).RevertForm(player)
    ELSEIF player.HasMagicEffect(BerserkArmorBeastTDanger)
	   (BerserkArmorBeastTDanger as SCRBerserkArmorB).RevertForm(player)
    ENDIF
    utility.wait(0.1)		;wait a small bit so revert form can be finished completely
    IF MEValueDefiner.GetValueInt() == 1		;get the value for activation of damage attributes and debuffs
       MEValueDefiner.SetValue(0)		 		;set the value to deactivate damage attributes and debuffs
    ENDIF

    copy_player_stats_to_replacement()                ; ###
	copy_player_gears_to_replacement()
    BerserkerRef.EquipItem(ArmorBerserkerWBoots2, true, true)
    BerserkerRef.EquipItem(ArmorBerserkerWCuirass2, true, true)
    BerserkerRef.EquipItem(ArmorBerserkerGauntlets2, true, true)
    BerserkerRef.EquipItem(ArmorBerserkWHelmet, true, true)
    BerserkerRef.AddPerk(AllowShoutingPerk)
    BerserkerRef.AddPerk(Berserk_NPC_FallDmg)

    BerserkerAlias.Clear()
    BerserkerAlias.ForceRefTo(BerserkerRef)        ; to attach scripts to the berserker
;    (MTE_Quest_Follower_System as MTE_Quest_Follower_System_Script).Update_LC_playerreplacement(BerserkerRef)  ;leftover code from MTEO for follower system, not needed here

;For some reason SetAngle() doesnt work for X angle so I'll use TranslateTo to set this angle
    float aX = player.GetAngleX()
    float aY = player.GetAngleY()
    float aZ = player.GetAngleZ()

    fx = player.GetPositionX()
    fy = pLayer.GetPositionY()
    fz = player.GetPositionZ()

    BerserkerRef.TranslateTo(fx,fy,fz, aX,aY,aZ, 300.0)    

    Game.SetCameraTarget(BerserkerRef)    ; MUST be runned on an enabled Object
    Game.ForceFirstPerson()             ; toggle to first person. Force the game to update the camera when we..
    Utility.Wait(0.1)
 
    Game.ForceThirdPerson()             ; force third person

    IF ( PlatRef )
        PlatRef.Delete()
        PlatRef = None
    ENDIF

    PlatRef = player.PlaceAtMe(DwePlatMid01, 1, TRUE, TRUE)
    PlatRef.SetPosition(fx, fy, fz + 3000.0)
    PlatRef.SetAngle(0.0, 0.0, 0.0)
    PlatRef.Enable()

    aX = player.GetAngleX()
    aY = player.GetAngleY()
    aZ = player.GetAngleZ()

    player.TranslateTo(fx, fy, fz + 3300.0, aX, aY, aZ, 1000.0, 0.0)

    player_has_control = False
    RegisterForSingleUpdate(3.0)

;    utility.wait(5.0)    ; just don't forget to overwrite still active translations (which is quiet unlikly ) in Gaincontrol()
;;    debug.messagebox("loosing Controls done")

    mutex_Add()
	Berserk_Moral_Transformed.SetValue(1)
ENDFUNCTION


;----------------------------------------------
Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (non array version)
;----------------------------------------------
    bool bFemale = (player.GetActorBase().GetSex() == 1)
    int        i = player.GetEquippedItemType(1)            ; right hand
    actorBase AB

IF ( bFemale )
    IF     (i == 0)
                                    AB = Berserk_Player_Loose_Controls_Unarmed_F        ; 0
	ELSEIF (i == 11)
									Weapon j = player.GetEquippedWeapon() as Weapon
									player.UnEquipItem(j, False, True)
									AB = Berserk_Player_Loose_Controls_Unarmed_F		; 11 torch, unequip then return to unarmed
    ELSEIF (i == 8)
                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ; 8  staff
    ELSEIF (i == 9)
                                    AB = Berserk_Player_Loose_Controls_Mage_F           ; 9  spell
    ELSEIF (i == 7) || (i == 12)
                                    AB = Berserk_Player_Loose_Controls_Ranger_F         ; 7, 12
    ELSEIF (i >= 1) && (i <= 6)
        i = player.GetEquippedItemType(0)                    ; left hand
        IF (i == 8) || (i == 9)
                                    AB = Berserk_Player_Loose_Controls_Spellsword_F     ;
        ELSE
                                    AB = Berserk_Player_Loose_Controls_Melee_F             ; 1, 2, 3, 4, 5, 6
        ENDIF
    ENDIF
ELSE
    IF     (i == 0)
                                    AB = Berserk_Player_Loose_Controls_Unarmed_M
	ELSEIF (i == 11)
									Weapon j = player.GetEquippedWeapon() as Weapon
									player.UnEquipItem(j, False, True)
									AB = Berserk_Player_Loose_Controls_Unarmed_M
    ELSEIF (i == 8)
                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
    ELSEIF (i == 9)
                                    AB = Berserk_Player_Loose_Controls_Mage_M
    ELSEIF (i == 7) || (i == 12)
                                    AB = Berserk_Player_Loose_Controls_Ranger_M
    ELSEIF (i >= 1) && (i <= 6)
        i = player.GetEquippedItemType(0)                    ; left hand
        IF (i == 8) || (i == 9)
                                    AB = Berserk_Player_Loose_Controls_Spellsword_M
        ELSE
                                    AB = Berserk_Player_Loose_Controls_Melee_M
        ENDIF
    ENDIF
ENDIF

IF ( !AB )
    RETURN None
ENDIF
;---------
    RETURN player.PlaceAtMe(AB, 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
ENDFUNCTION

;;----------------------------------------------
;Actor FUNCTION myF_PlaceBerserker(Actor player)  ; internal helper (array version) ; not used for now, i prefer the non array version
;;----------------------------------------------
;    int i = player.GetEquippedItemType(1)            ; right hand
;    int n = -1

;    IF     (i == 0)
;                                    n = 0        ; Berserk_Player_Loose_Controls_Unarmed_M       0 -- 5  Berserk_Player_Loose_Controls_Unarmed_F
;    ELSEIF (i == 8)
;                                    n = 4        ; Berserk_Player_Loose_Controls_Spellsword_M    4 -- 9 Berserk_Player_Loose_Controls_Spellsword_F
;    ELSEIF (i == 9)
;                                    n = 3        ; Berserk_Player_Loose_Controls_Mage_M          3 -- 8 Berserk_Player_Loose_Controls_Mage_F
;    ELSEIF (i == 7) || (i == 12)
;                                    n = 2        ; Berserk_Player_Loose_Controls_Ranger_M        2 -- 7  Berserk_Player_Loose_Controls_Ranger_F
;    ELSEIF (i >= 1) && (i <= 6)
;        i = player.GetEquippedItemType(0)            ; left hand
;        IF (i == 8) || (i == 9)
;                                    n = 4        ; Berserk_Player_Loose_Controls_Spellsword_M    4 -- 9 Berserk_Player_Loose_Controls_Spellsword_F
;        ELSE
;                                    n = 1        ; Berserk_Player_Loose_Controls_Melee_M            1 -- 6  Berserk_Player_Loose_Controls_Melee_F
;        ENDIF
;    ENDIF

;IF (n < 0)
;    RETURN None        ; What is about Torch?
;ENDIF
;;---------
;    IF (player.GetActorBase().GetSex() == 1)
;        n = n + 6            ; female actorbase position
;    ENDIF

;    RETURN player.PlaceAtMe(Berserk_Player_Loose_Controls_Array[n], 1, abForcePersist=True, abInitiallyDisabled=True) as Actor
;ENDFUNCTION


;------------------------------------------
FUNCTION copy_player_stats_to_replacement()  ; internal helper, copy Player stats to Loose Control replacement
;------------------------------------------
IF ( !BerserkerRef )
    RETURN    ; - STOP -
ENDIF
;---------------------
string[] a = new string[11]                ; a = primevalues + secvalues
    a[0] = "Health"
    a[1] = "Stamina"
    a[2] = "Magicka"
    a[3] = "UnarmedDamage"
    a[4] = "DamageResist"
    a[5] = "DiseaseResist"
    a[6] = "PoisonResist"
    a[7] = "FireResist"
    a[8] = "ElectricResist"
    a[9] = "FrostResist"
    a[10]= "MagicResist"

    actor player = Game.GetPlayer()
    float f                                ; f = pval

int i = 0
    WHILE (i < 3)
        f = player.GetBaseActorValue(a[i])            ; get player base actor value
        BerserkerRef.SetActorValue(a[i], f)

        float fc = player.GetActorValue(a[i])
        player.RestoreActorValue(a[i], 100000)        ; to get the modified maxValue

        f = player.GetActorValue(a[i])                ; get current player max actor value
        player.DamageActorValue(a[i], f - fc)
        BerserkerRef.RestoreActorValue(a[i], 100000)
;        ------------
        float fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.ModActorValue(a[i], f - fw)
        BerserkerRef.RestoreActorValue(a[i], 100000)

        fw = BerserkerRef.GetActorValue(a[i])
        BerserkerRef.DamageActorValue(a[i], fw - fc)

        i = i + 1
    ENDWHILE

    WHILE (i < a.Length)
        f = player.GetActorValue(a[i])               ; get current player actor value
        BerserkerRef.ForceActorValue(a[i], f)        ; force that to the berseker
        i = i + 1
    ENDWHILE
ENDFUNCTION


;---------------------------------------------
Form FUNCTION GetWornAmmo(Formlist AmmoList)	; ammo list for copy player gears function
;---------------------------------------------
	Form Entry = NONE
	Int index = 0
	WHILE index < AmmoList.GetSize()
		Entry = AmmoList.GetAt(Index)
		IF Entry.IsEquipped()
			index = AmmoList.GetSize() 	; got what we need stop loop
		ELSE
			Entry = NONE	; erase stored entry as we do not need it
		ENDIF
		index += 1
	ENDWHILE
	RETURN Entry
ENDFUNCTION


;---------------------------------------------
FUNCTION copy_player_gears_to_replacement()		; copy Player gears to Loose Control replacement
;---------------------------------------------
  actor player = Game.GetPlayer()
  Form WornAmmo = GetWornAmmo(WornAmmoList)
  IF ( !BerserkerRef )
    RETURN    ; - STOP -
  ENDIF
  IF (player.GetWornForm(0x00000200) != None)
	  Armor Shield = player.GetWornForm(0x00000200) as Armor
	  BerserkerRef.AddItem(Shield, absilent=true)
	  BerserkerRef.EquipItem(Shield, true, true)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetWornForm(0x00000020) != None)
	  Armor Amulet = player.GetWornForm(0x00000020) as Armor
	  BerserkerRef.AddItem(Amulet, absilent=true)
	  BerserkerRef.EquipItem(Amulet, false, true)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetWornForm(0x00000040) != None)
	  Armor Ring = player.GetWornForm(0x00000040) as Armor
	  BerserkerRef.AddItem(Ring, absilent=true)
	  BerserkerRef.EquipItem(Ring, false, true)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetEquippedWeapon() != None)
	  Weapon RWeapon = player.GetEquippedWeapon() as Weapon
	  BerserkerRef.AddItem(RWeapon, absilent=true)
	  BerserkerRef.EquipItem(RWeapon, true, true)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetEquippedWeapon(true) != None)
	  Weapon LWeapon = player.GetEquippedWeapon(true) as Weapon
	  BerserkerRef.AddItem(LWeapon, absilent=true)
	  BerserkerRef.EquipItem(LWeapon, true, true)
;     Debug.MessageBox("...")
  ENDIF
  IF WornAmmo ;valid ammo
	  BerserkerRef.AddItem(WornAmmo, 100, absilent=true) ;could get count of ammo on player or just give a fixed ammount that should last the duration of the berserker status
	  BerserkerRef.EquipItem(WornAmmo, true, true)
  ENDIF
  IF (player.GetEquippedSpell(0) != None)
	  Spell LSpell = player.GetEquippedSpell(0) as Spell
	  BerserkerRef.AddSpell(LSpell)
	  BerserkerRef.EquipSpell(LSpell, 0)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetEquippedSpell(1) != None)
	  Spell RSpell = player.GetEquippedSpell(1) as Spell
	  BerserkerRef.AddSpell(RSpell)
	  BerserkerRef.EquipSpell(RSpell, 1)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetEquippedSpell(2) != None)
	  Spell OSpell = player.GetEquippedSpell(2) as Spell
	  BerserkerRef.AddSpell(OSpell)
	  BerserkerRef.EquipSpell(OSpell, 2)
;     Debug.MessageBox("...")
  ENDIF
  IF (player.GetEquippedShout() != None)
	  Shout Shouts = player.GetEquippedShout() as Shout
	  BerserkerRef.AddShout(Shouts)
	  BerserkerRef.EquipShout(Shouts)
;     Debug.MessageBox("...")
  ENDIF
ENDFUNCTION 

 

 

 

2. SCRBerserkArmorCheckIfSet.

This script is to add the spell which trigger the transformation to the player. I need to give a bit more explanations about this. So, the mod i'm trying to make is a transformation feature which is casted by a spell. The transformation has chance to make the player "losing control". The spell to transform is added to the player when he/she is wearing a certain armor. This script (ie. SCRBerserkArmorCheckIfSet) checks if the player has worn the armor properly, then add the spell. In addition, this script also contains additional features such as initialising the loose control script (ie. Berserk_Loose_Control_Script) and giving chance to transform when getting hit.

 

 

Scriptname SCRBerserkArmorCheckIfSet extends  ObjectReference

Quest Property Berserk_Quest_Loose_Control Auto
Spell Property BerserkerArmorBeastChange  Auto  

GlobalVariable Property TargetLvlMult_AutoT Auto
GlobalVariable Property HealthThreshold_AutoT Auto
GlobalVariable Property RandomChance_AutoT Auto

Bool LC_system_active = False

Actor Player

Event OnInit()
	Player = Game.GetPlayer()
EndEvent

Event OnEquipped(Actor akActor)
	akActor.AddSpell(BerserkerArmorBeastChange)
	akActor.EquipSpell(BerserkerArmorBeastChange, 2)
	(Berserk_Quest_Loose_Control as Berserk_Loose_Control_Script).Initialise()
EndEvent

Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
	If (Player.IsInCombat() == True) && (Player.GetActorValuePercentage("Health") <= HealthThreshold_AutoT.GetValue()) && (Player.GetCombatTarget().GetLevel() >= (TargetLvlMult_AutoT.GetValue() * Player.GetLevel())) && (Player.GetCurrentLocation() != WhiterunJorrvaskrLocation) && (Player.IsDead() == False) && (Utility.RandomFloat(1.0, 100.0) <= RandomChance_AutoT.GetValue())
		BerserkerArmorBeastChange.Cast(Player)
	Else
		Return
	EndIf
EndEvent

Event OnUnequipped(Actor akActor)
	akActor.RemoveSpell(BerserkerArmorBeastChange)
        If (Berserk_Quest_Loose_Control as Berserk_Loose_Control_Script)
            LC_system_active = False
        EndIf
EndEvent 

 

 

 

3. Berserk_RefAlias_Playerreplacement_Script

This script is a ref alias for the player replacement when he/she's losing control and being replaced by the NPC replacer.

 

 

Scriptname Berserk_RefAlias_Playerreplacement_Script extends ReferenceAlias  

Actor Property PlayerRef auto
Quest Property Berserk_Quest_Loose_Control auto
Formlist Property WornAmmoList Auto    ;this list starts out empty, gets filled as ammo is equipped by the player ;must be placed in ref alias script for lost control script

Event OnObjectEquipped(Form akBaseObject, ObjectReference akReference)
	If akBaseObject as Ammo 
		WornAmmoList.AddForm(akBaseObject)
	EndIf
EndEvent

Event OnDying(Actor akKiller)
  (Berserk_Quest_Loose_Control as Berserk_Loose_Control_Script).player_replacement_was_killed((self.GetReference()) as actor, akKiller)
EndEvent

Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
  utility.wait(0.1)
  (Berserk_Quest_Loose_Control as Berserk_Loose_Control_Script).update_player_health()
EndEvent

Event OnDetachedFromCell()
  (Berserk_Quest_Loose_Control as Berserk_Loose_Control_Script).update_player_pos()
EndEvent 

 

 

 

4. Berserk_Effect_Warningspell_Script

This script is for warning spell which will be casted when the player is about to lose control.

 

 

Scriptname Berserk_Effect_Warningspell_Script extends ActiveMagicEffect  

Actor Property PlayerREF Auto

;GlobalVariable Property Berserk_Config_LC_Warning Auto		;already covered by Berserk_Config_Moral_Warningtime exists in lose control script & MCM script
GlobalVariable Property Berserk_Comp_Dawnguard Auto
GlobalVariable Property Berserk_Config_Sounds Auto

Sound Property UIHealthHeartbeatALPSD Auto		;switch to something more suitable, such as heartbeat sound
Sound Property UIHealthHeartbeatBLPSD Auto		;switch to something more suitable, such as heartbeat sound

Spell Property Berserk_Spell_Warningspell Auto

MagicEffect Property Berserk_Effect_Warningspell Auto

ImageSpaceModifier Property DA10HauntingISMDFadeIn Auto
ImageSpaceModifier Property DA10HauntingISMDLoop Auto
ImageSpaceModifier DLC1AurielsEclipseImod01
ImageSpaceModifier Property WerewolfWarningImod Auto

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Event OnInit()

   Actor TargetLock = GetTargetActor()
   If (TargetLock == None)
      TargetLock.dispelspell(Berserk_Spell_Warningspell)
      Return
   ElseIF (TargetLock == PlayerREF)
      RegisterForSingleUpdate(5)
   Else
      TargetLock.dispelspell(Berserk_Spell_Warningspell)
      Return
   EndIf

EndEvent

;State Running
   Event OnUpdate()

;Dawnguard DLC

   If Berserk_Comp_Dawnguard.GetValueInt() == 1
      DLC1AurielsEclipseImod01 = Game.GetFormFromFile(0x02003DCD, "Dawnguard.esm") as ImageSpaceModifier
   Else
      DLC1AurielsEclipseImod01 = WerewolfWarningImod
   EndIf

;End of Dawnguard DLC

      DA10HauntingISMDFadeIn.ApplyCrossFade(12.0)

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      DA10HauntingISMDLoop.ApplyCrossFade(16.0)

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      Sounds()
      If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
         Remove_Imods()
         Return
      EndIf

      RegisterForSingleUpdate(3)

   EndEvent
   
   Event OnEffectFinish(Actor akTarget, Actor akCaster)
   
      Remove_Imods()
   
   EndEvent
;EndState

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function Sounds()

   If !PlayerREF.HasMagicEffect(Berserk_Effect_Warningspell)
      Return
   EndIf
   Int RandomGrowlSound = Utility.RandomInt(1, 4)
   DLC1AurielsEclipseImod01.Remove()
   DLC1AurielsEclipseImod01.Apply()
   Game.ShakeCamera(afStrength = 1, afDuration = 2)
   UIHealthHeartbeatBLPSD.Play(PlayerREF)
   If RandomGrowlSound == 1 && Berserk_Config_Sounds.GetValueInt() == 1
      Int SoundID1 = UIHealthHeartbeatALPSD.Play(PlayerREF)
      Sound.SetInstanceVolume(SoundID1, 0.5)
   EndIf
   Utility.Wait(4)

EndFunction

Function Remove_Imods()
   DA10HauntingISMDFadeIn.Remove()
   DA10HauntingISMDLoop.Remove()
   DLC1AurielsEclipseImod01.Remove()
   Utility.Wait(5)
   DA10HauntingISMDFadeIn.Remove()
   DA10HauntingISMDLoop.Remove()
   DLC1AurielsEclipseImod01.Remove()
EndFunction 

 

 

 

I still have additional script to add kill skill gain feature, but it's still on the plan so i can't upload it yet. Currently, i'm still wondering whether i have put function Initialise() and TransformBack() from Berserk_Loose_Control_Script properly. Initialise() acts as an "Start" button for lose control system,and TransformBack() acts as "Stop" button.

I really hope that you can check on these and give me feedbacks or corrections if necessary. Thanks in advance. :smile:

Edited by qwertypol012
Link to comment
Share on other sites

At first let us make clear the dependencies and scripting order.

 

(1) SCRBerserkArmorCheckIfSet, renamed as

qlBerserk_ArmorScript

 

Scriptname qlBerserk_ArmorScript extends ObjectReference
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/
; is attached to an armor, which controls the spell "BerserkerArmorBeastChange"

;"This script is to add the spell which trigger the transformation to the player. I need to give a bit more explanations about this.
; So, the mod i'm trying to make is a transformation feature which is casted by a spell. The transformation has chance to make the player "losing control".
; The spell to transform is added to the player when he/she is wearing a certain armor.
; This script (ie. SCRBerserkArmorCheckIfSet) checks if the player has worn the armor properly, then add the spell.
; In addition, this script also contains additional features such as initialising the loose control script (ie. Berserk_Loose_Control_Script) and
; giving chance to transform when getting hit." (qwertypol012)

  Quest PROPERTY myQuest auto    ; the special quest "Berserk_Quest_Loose_Control"


; -- EVENTs -- 1 + "Waiting"

EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
; received when this object enters, exits, or changes containers

IF (akNewContainer == Game.GetPlayer() as ObjectReference)
ELSE
    gotoState("")                ; ### STATE ###
    RETURN    ; - STOP -
ENDIF
;---------------------
    gotoState("Waiting")        ; ### STATE ###
    IF (myQuest) && !myQuest.IsRunning()
        myQuest.Start()
    ENDIF
ENDEVENT


;====================================
state Waiting    ; for player only
;============
EVENT OnEquipped(Actor akActor)
    IF ( myQuest )
    ELSE
        Debug.Trace("qlBerserk: OnEquipped(1) - quest property is missing!")
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF myQuest.IsRunning
    ELSE
        Debug.Trace("qlBerserk: OnEquipped(2) - quest is not running!")
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (myQuest as qlBerserk_QuestScript)
    ELSE
        Debug.Trace("qlBerserk: OnEquipped(3) - quest script 'qlBerserk_QuestScript' not found!")
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    (myQuest as qlBerserk_QuestScript).myF_Init(akActor, TRUE)
ENDEVENT


EVENT OnUnequipped(Actor akActor)
    IF (myQuest as qlBerserk_QuestScript)
    ELSE
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    (myQuest as qlBerserk_QuestScript).myF_Init(akActor, False)
ENDEVENT
;=======
endState

 

 

As you can see I removed some code to make it more clean. I wonder if the OnHit() event would be triggered for this armor script.

 

(2) Berserk_Effect_Warningspell_Script, renamed as

qlBerserk_EffectScript

 

Scriptname qlBerserk_EffectScript extends ActiveMagicEffect
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/
; "This script is for warning spell which will be casted when the player is about to lose control."

  GlobalVariable PROPERTY Berserk_Comp_Dawnguard auto        ; DLC1 has been loaded, GetValue() = 1.0
  GlobalVariable PROPERTY Berserk_Config_Sounds  auto

  MagicEffect PROPERTY Berserk_Effect_Warningspell auto      ; effect that this script should be attached

; switch to something more suitable, such as heartbeat sound
  Sound PROPERTY UIHealthHeartbeatALPSD auto
  Sound PROPERTY UIHealthHeartbeatBLPSD auto

  ImageSpaceModifier PROPERTY DA10HauntingISMDFadeIn auto
  ImageSpaceModifier PROPERTY DA10HauntingISMDLoop   auto
  ImageSpaceModifier PROPERTY WerewolfWarningImod    auto
  ImageSpaceModifier     DLC1AurielsEclipseImod01

  Bool bReady


; -- EVENTs -- 2 + "Running"

EVENT OnEffectStart(Actor akTarget, Actor akCaster)
IF (akTarget == Game.GetPlayer())
ELSE
    self.Dispel()                                           ; akTarget.DispelSpell(Berserk_Spell_Warningspell)
    RETURN    ; - STOP -    target is not the player
ENDIF
;---------------------
    bReady = TRUE
    gotoState("Running")            ; ### STATE ###
    RegisterForSingleUpdate(5.0)                            ; wait 5 seconds, before triggering update event                        
ENDEVENT


EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
    myF_Remove()
ENDEVENT


;==========================================
State Running
;============
EVENT OnUpdate()
    IF ( !DLC1AurielsEclipseImod01 )
        myF_SetImod01()
    ENDIF

IF myF_IsEffectGone(DA10HauntingISMDFadeIn, 12.0)      ; wait max. three times inside
    RETURN    ; - STOP -
ENDIF
;---------------------
IF myF_IsEffectGone(DA10HauntingISMDLoop, 16.0)        ; wait max. four times inside
    RETURN    ; - STOP -
ENDIF
;---------------------
    RegisterForSingleUpdate(3.0)                       ; trigger event again in 3 seconds
ENDEVENT
;=======
endState


; -- FUNCTIONs -- 4

;-----------------------
FUNCTION myF_SetImod01()
;-----------------------
    DLC1AurielsEclipseImod01 = WerewolfWarningImod

; *** Dawnguard DLC
    IF (Berserk_Comp_Dawnguard.GetValue() == 1)
        form fm = Game.GetFormFromFile(0x02003DCD, "Dawnguard.esm")
        IF (fm as ImageSpaceModifier)
            DLC1AurielsEclipseImod01 = fm as ImageSpaceModifier
        ELSE
            Debug.Trace("Error: Cannot find Dawnguard imod 'DLC1AurielsEclipseImod01'!")
        ENDIF
    ENDIF
; *** End of Dawnguard DLC
ENDFUNCTION


;--------------------------------------------------------------
Bool FUNCTION myF_IsEffectGone(ImageSpaceModifier ISM, Float f)
;--------------------------------------------------------------
    ISM.ApplyCrossFade(f)

int i = (f / 4.0) as Int        ; i loop counter
    WHILE (i > 0) && Game.GetPlayer().HasMagicEffect(Berserk_Effect_Warningspell)
        myF_Play()
        Utility.Wait(4.0)
        i = i - 1
    ENDWHILE

IF Game.GetPlayer().HasMagicEffect(Berserk_Effect_Warningspell)
    Return False        ; ***
ENDIF
;---------
    myF_Remove()
    Return TRUE         ; *T*
ENDFUNCTION


;--------------------
FUNCTION myF_Remove()
;--------------------
IF ( !bReady )
    RETURN    ; - STOP -
ENDIF
;---------------------
    bReady = False
    gotoState("")                    ; ### STATE ###

    DA10HauntingISMDFadeIn.Remove()
    DA10HauntingISMDLoop.Remove()
    IF ( DLC1AurielsEclipseImod01 )
        DLC1AurielsEclipseImod01.Remove()
    ENDIF
ENDFUNCTION


;------------------
FUNCTION myF_Play()
;------------------
    DLC1AurielsEclipseImod01.Remove()
    DLC1AurielsEclipseImod01.Apply()

    objectReference playerRef = Game.GetPlayer() as ObjectReference

    Game.ShakeCamera(afStrength = 1, afDuration = 2)
    UIHealthHeartbeatBLPSD.Play(playerRef)

IF (Berserk_Config_Sounds.GetValue() == 1) && (Utility.RandomInt(1, 4) == 1)    ; RandomGrowlSound 25% chance
    int i = UIHealthHeartbeatALPSD.Play(playerRef)                              ; i = SoundID1
    IF (i > 0)
        Sound.SetInstanceVolume(i, 0.5)
    ENDIF
ENDIF
ENDFUNCTION

 

 

 

I remove the OnInit() event and added instead the OnEffectStart() event, which is much more useful to take for ActiveMagicEffect scripts.

 

(3) Berserk_RefAlias_Playerreplacement_Script, renamed as How much is this script used and for what RefAliases?

qlBerserk_PlayerAliasScript

 

Scriptname qlBerserk_PlayerAliasScript extends ReferenceAlias
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/
; This script is a ref alias for the player replacement when he/she is losing control and being replaced by the NPC replacer.

  GlobalVariable PROPERTY TargetLvlMult_AutoT   auto
  GlobalVariable PROPERTY HealthThreshold_AutoT auto
  GlobalVariable PROPERTY RandomChance_AutoT    auto

  Location PROPERTY WhiterunJorrvaskrLocation auto
  Quest myQuest

 ;Formlist PROPERTY WornAmmoList auto  ; must be placed here for lost control script, starts out empty


; -- EVENTs -- 5 + "Busy"

EVENT OnInit()
    myQuest = self.GetOwningQuest()
ENDEVENT


EVENT OnDetachedFromCell()   ; no idea whether this will be triggered within a playerAlias script !!
IF (myQuest as qlBerserk_QuestScript)
ELSE
    RETURN    ; - STOP -
ENDIF
;---------------------
    (myQuest as qlBerserk_QuestScript).update_player_pos()
ENDEVENT


EVENT OnDying(Actor akKiller)
IF (myQuest as qlBerserk_QuestScript)
ELSE
    RETURN    ; - STOP -
ENDIF
;---------------------
    (myQuest as qlBerserk_QuestScript).player_replacement_was_killed(self.GetActorReference(), akKiller)
ENDEVENT


EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4)
    actor player = self.GetActorReference()

IF !player.IsInCombat() || player.IsDead()
    RETURN    ; - STOP -    player is not in combat /or/ just dead
ENDIF
;---------------------
IF (player.GetActorValuePercentage("Health") <= HealthThreshold_AutoT.GetValue())
    RETURN    ; - STOP -
ENDIF
;---------------------
    myF_Action(akAggressor, player)

    gotoState("Busy")                ; ### STATE ###
    (myQuest as qlBerserk_QuestScript).update_player_health()
    gotoState("")                    ; ### STATE ###
ENDEVENT


;EVENT OnObjectEquipped(Form akBaseObject, ObjectReference akReference)
;IF (akBaseObject as Ammo)
;    WornAmmoList.AddForm(akBaseObject)        ; gets filled as ammo is equipped by the player
;ENDIF
;ENDEVENT


;====================================
state Busy        ; with hit action
;=========
;Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4)
ENDEVENT
;=======
endState


; -- FUNCTION --

;-------------------------------------------------------------
FUNCTION myF_Action(ObjectReference akAggressor, Actor player)  ; internal helper
;-------------------------------------------------------------
IF (player.GetCurrentLocation() == WhiterunJorrvaskrLocation)
    RETURN    ; - STOP -    player is in special location, brawl possible
ENDIF
;---------------------
IF (akAggressor as Actor)
ELSE
    RETURN    ; - STOP -    player was hit by non-actor, could be happens
ENDIF
;---------------------
IF ((akAggressor as Actor).GetLevel() < player.GetLevel() * TargetLvlMult_AutoT.GetValueInt())
    RETURN    ; - STOP -    player level is higher then aggressor level
ENDIF
;---------------------
IF (Utility.RandomInt(1, 100) > RandomChance_AutoT.GetValueInt())
    RETURN    ; - STOP -    out of chance to cast spell
ENDIF
;---------------------
    BerserkerArmorBeastChange.Cast(player)
    Utility.Wait(0.1)  
ENDFUNCTION

 

 

 

This is the right place for the OnHit() event code from armor script above. I cannot see any sense to use OnObjectEquipped() here.

 

(4) Berserk_Loose_Control_Script, renamed as

qlBerserk_QuestScript

 

Scriptname qlBerserk_QuestScript extends Quest Conditional
; https://forums.nexusmods.com/index.php?/topic/7301906-how-to-properly-script-a-custom-conditional-system-which-will-be-used-by-a-quest-script/

;Based on MTE_Quest_Loose_Control_Script from Moonlight Tales Essentials Overhauled (MTEO) by ubuntufreakdragon, 
; most credits go to him for providing a robust base script for lose control functionality
; Huge credits go to ReDragon2013 (Nexusmods member) for helping me A LOT in editing and opimizing the script! That guy is really amazing :D

;------------------------------------------
FUNCTION myF_Init(Actor player, Bool bInit)  ; external called by "qlBerserk_ArmorScript" only
;------------------------------------------
    IF ( bInit )
        player.AddSpell(BerserkerArmorBeastChange)
        player.EquipSpell(BerserkerArmorBeastChange, 2)
;;;        BerserkerArmorBeastChange.Cast(akActor)                ; maybe CAST would be better here
        Initialise()
    ELSE
        player.RemoveSpell(BerserkerArmorBeastChange)
    ENDIF
ENDFUNCTION


;--------------------
FUNCTION Initialise()  ; the LC system until Force_control_to_player() is called
;--------------------
; setting up some time until, the loose control check runs first

;If (Berserk_LooseControlMoral_Enable.GetValueInt() == 1)    ; currently checked in mainloop
;    return
;EndIf

;   MoralScore=0        ;no more set as an assignment variable, no need to be initialized, because it seems that game stats can be tracked & accumulated without using assignment
    LC_system_active = TRUE
    RegisterForSingleUpdateGameTime( Utility.RandomFloat(0.25, 0.5) )    ; run first check after a quarter to a half hour
ENDFUNCTION

; .. cut here

 

 

 

This quest script only with two functions, I believe there is still some work to make code inside more understandable. Some code of this is truly strange for me.

Edited by ReDragon2013
Link to comment
Share on other sites

Well, i need to elaborate it (ie. your edited scripts) at first (that's a lot of new logics and conditions :sweat:, but that's good if it improve the scripts :laugh: ) ... I couldn't imagine such simple-looking scripts from me could become something more complex-looking :pinch:


Anyway, you renamed the scripts with "qlxxxx". What does "ql" refer to btw?



I also have some feedbacks to what you wrote above:

1. You asked: "How much is this script used and for what RefAliases?" and "I cannot see any sense to use OnObjectEquipped()".

Berserk_RefAlias_Playerreplacement_Script or qlBerserk_PlayerAliasScript is only used for 1) calling function "player_replacement_was_killed" ie. if the player was killed during losing control, 2) updating the player health when the npc replacer is getting hit, 3) updating the player pos when the npc replacer goes too far (or enter a new cell i guess), 4) and to check if the player is equipping any ammo.

Ammo equpping check is used by Berserk_Loose_Control_Script or qlBerserk_QuestScript for the function "copy_player_gears_to_replacement()", which is if the player is equipping any ammo then it will be added to the npc replacer when lose control is triggered. So yeah, that function is needed, and it's put in the ref alias script to check if the player is equipping any ammo.


2. You wrote: "This is the right place for the OnHit() event code from armor script above."

I'm not sure if you've understood it correctly, but i just want to clarify that OnHit() in SCRBerserkArmorCheckIfSet is actually different with OnHit() in Berserk_RefAlias_Playerreplacement_Script. As the name of each script suggests, the first one is for the player, and the second one is for the NPC replacer. In other words, OnHit() in SCRBerserkArmorCheckIfSet runs when the player is getting hit (means that he/she is not losing control), while OnHit() in Berserk_RefAlias_Playerreplacement_Script runs when the NPC replacer (not the player) is getting hit (means that the player is losing control and hence is replaced by the NPC replacer) and then update the player's health based on the damage done to the NPC replacer. So i think we need 2 OnHit() events in separate scripts, right?


3. You wrote: "EVENT OnDetachedFromCell() ; no idea whether this will be triggered within a playerAlias script"

This is the original event from the player replacement ref alias script of MTEO by ubuntufreakdragon, and it does seem to work for some reason. Though i also do have no clue about this anyway. But do note that the function "update_player_pos()" called by this event is also called within Berserk_Loose_Control_Script or qlBerserk_QuestScript by OnUpdate() and OnUpdateGameTime() events. So i guess those two also contribute to make it work.


4. You wrote: "Some code of this is truly strange for me."

Which one do you mean?



I do have an additional question:

In the last edit, in Berserk_Loose_Control_Script, i tweaked MoralScore variable under Initialise() and Force_control_to_player() to set it into 0. So it's: MoralScore = 0

Is this necessary? I just want to restore it to zero so that it will be filled again when the lose control roll is running again.

Edited by qwertypol012
Link to comment
Share on other sites

ql is refering to your nickname "qwertypol012", keep in mind every script is stored in the same script folder.

In case two moders use the same script name in their mods, only the script from last loaded mod (esp-file) will be used ingame. That would be a problem.

 

You added "int MoralScore" as global script variable, not a good idea. Why? It is used as local function variable "float MoralScore", which makes really confusing.

 

About hit event your are right, this can be triggered also in ObjectReference scripts.

I need some time to update the scripts according to your explanation.

 

You wrote: "Berserk_RefAlias_Playerreplacement_Script or qlBerserk_PlayerAliasScript is only used for .."

My question was, how many RefAliases may have attached this script?

Link to comment
Share on other sites

"In case two moders use the same script name in their mods, only the script from last loaded mod (esp-file) will be used ingame. That would be a problem."

I see. You got a point. That nickname will really help.

 

"You added "int MoralScore" as global script variable, not a good idea. Why? It is used as local function variable "float MoralScore", which makes really confusing."

Uh, right. But it's actually remnants from your edited script of Berserk_Loose_Control_Script. You put "Int KillScore" as script variable, then put "float KillScore" as local function variable, then i renamed it to "MoralScore" and removed some math calculations from it, making it more simple since now it's purely calculated based on game stats, hence no need for additional values except from the used game stats and related variables.

So, should it be a script variable or local function variable? But it's used by at least 2 functions, so will it work without it's set as script variable?

 

"My question was, how many RefAliases may have attached this script?"

I'm not sure if i understand your question properly, but i can only say that the player replacement ref alias script is only meant for the npc replacer. If it doesn't bother you, mind to explain to me a bit about RefAliases? I think i'll make some mistakes if i don't understand about RefAliases properly (i tried to read the article in creation kit website but it's a bit complicated, a bit unfathomable for me).

 

 

Btw, i actually has a player ref alias script which currently only serves as a mean to give severe damage to the player when he's "overtransform". Here's the script:

Scriptname Berserk_PlayerAliasScript extends ReferenceAlias
{must be set as ref alias script for a quest script other than lost control script}

Actor Property PlayerRef Auto
MagicEffect Property BerserkArmorBeastTDanger Auto
Spell Property BerserkerSelfRepairDangerous Auto

Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
	If (PlayerRef.HasMagicEffect(BerserkArmorBeastTDanger))
		BerserkerSelfRepairDangerous.Cast(PlayerRef)
	Else
	    Return
	EndIf
EndEvent

 

 

This also needs more explanations. In addition to everything i have written above (regarding the mod i'm trying to make), there are actually 2 kinds of transformation magic effect. The first one is normal transformation, and the second one is a dangerous transformation. Both are casted from a single spell (let's call it Transformation Spell), but with different conditions so that only one of them will be triggered whenever the spell is casted.

 

As i have said earlier (or so i think), the transformations leave after effects which will be triggered whenever the transformation ends. They are negative effects from some magic effects which are casted from a spell. If you noticed in the loose control script (Berserk_Loose_Control_Script), there's "BerserkerLostControlAfterEffects" spell. This is the spell to cast these negative effects (let's call it "After-effects Spell"). These negative effects have durations and will wear off once the durations end. If the player cast the Transformation Spell after these negative effects end, then the player will be transformed normally (normal transformation). If the player cast the Transformation Spell while the negative effects still active, then the player will go into the dangerous transformation. Basically, the dangerous transformation is only a more severe version of normal transformation. A notable feature from dangerous transformation is that the player will get additional damage when he's hit. To make it work, i had to make a script which does just this, and that's the script i posted above.

 

Actually, i'm still not sure where should i attach this script. The only thing i know is that it's different from the player replacement alias, so maybe i need to put it in a different quest script? But no clue if it can actually be simplified or included in any existing scripts.

 

 

Additonal notes:
To make things clearer (if it's still unclear), the lose control feature only runs when the player is in combat (i already set the condition in the lose control script). On top of that, there are also 3 additional conditions, and either 1 of them must be filled in order for lose control to run: 1) player has Transformation Spell (in other words, the player is wearing the armor, ie. the spell is added via the armor), 2) player is under normal transformation, or 3) player is under dangerous transformation.

What i really want to achieve from this is that, the lose control feature should work in ANY conditions (but only in combat), as long as the player is wearing the armor or he/she is being transformed. Note that when the player is being transformed, he/she will not wear the armor (it will be force-unequipped because the player will be force-equipped with other gears which replace the armor), so i need to put both transformation conditions on top of wearing the armor (a.k.a has the spell) condition.

 

TLDR, here's a basic simulation/scheme which summarizes all of above explanations:

 

Player wears the Armor => Transformation Spell added to Player

Player cast Transformation Spell => Player enter a Transformation state => Armor unequipped from Player, Armor2 equipped to Player

 

Transformation state:

1. Normal => casted after duration3 end

2. Dangerous => casted when duration3 still run

 

When Player is in Transformation state & if Player is in combat => has chance to Lose Control

 

If Lose Control is not triggered => Transformation state continues until its duration1 end

If Lose Control is triggered => Armor2 unequipped from Player, Armor equpped to Player, control taken from Player, Player replaced by NPC Replacer until its duration2 end

 

After duration1 end => Armor2 uneqipped from Player, Armor equipped to Player, Negative Effects casted to Player until its duration3 end

After duration2 end => NPC Replacer disabled, give control back to Player, Negative Effects casted to Player until its duration3 end

 

If duration3 still run & Player cast Transformation Spell => Player enter Dangerous Transformation

After duration3 end & Player cast Transformation Spell => Player enter Normal Transformation

 

When Player is in Dangerous Transformation & Player getting hit => Severe Damage spell casted to Player

 

 

I'll also post any additional scripts related to these functionalities (just in case if they're necessary):

1. qlBerserk_TransformScript

This is the script for Transformation Spell. Attached to both Normal Transformation and Dangerous Transformation magic effects.

 

 

Scriptname qlBerserk_TransformScript extends ActiveMagicEffect  
{Berserker Armor transformation}

Actor Property PlayerRef Auto
Actor Property PlayerEquipmentBrsk  Auto  

Armor Property ArmorBerserkerCuirass  Auto  
Armor Property ArmorBerserkerBoots  Auto  
Armor Property ArmorBerserkerWCuirass  Auto  
Armor Property ArmorBerserkerWBoots  Auto
Armor Property ArmorBerserkerGauntlets Auto
Armor Property ArmorBerserkSHelmet Auto
Armor Property ArmorBerserkerWCuirass2  Auto  
Armor Property ArmorBerserkerWBoots2  Auto  
Armor Property ArmorBerserkerGauntlets2  Auto  
Armor Property ArmorBerserkWHelmet  Auto  

EffectShader Property Deintegrate  Auto  

Explosion Property FXVampChangeExplosion Auto

Idle Property IdleVampireLordTransformation Auto

SPELL Property BerserkerArmorBeastChange  Auto  
SPELL Property BerserkerArmorRevertForm  Auto

bool bRemoveGhost
;bool abIsGhost

Event OnEffectStart(Actor akTarget, Actor akCaster)
;	Actor Player = Game.GetPlayer()
	if PlayerRef.IsEquipped(ArmorBerserkerCuirass) && PlayerRef.IsEquipped(ArmorBerserkerBoots)
		if akTarget.IsGhost()
		   bRemoveGhost = 0
		else
		   bRemoveGhost = 1
		   akTarget.GetActorBase().SetInvulnerable()
		   akTarget.SetGhost()
		endif
;		if abIsGhost == false
;		   akTarget.SetGhost()
;		endif
		RegisterForAnimationEvent(akTarget, "SetRace")
		akTarget.PlayIdle(IdleVampireLordTransformation)
		akTarget.RestoreActorValue("health", 5000)
		Utility.Wait(10)
		TransformIfNecessary(akTarget)
;   	if (akTarget.GetWornForm(0x00000008) != none)
;    	   Debug.Messagebox("Gauntlets are equipped")
;   	else
;       	Debug.MessageBox("Gauntlets are not equipped or something is wrong")
;   	endif
;	check if gauntlets are being worn or not, then add and equip them to the dummy if they are
		if (akTarget.GetWornForm(0x00000008) != None)
			Armor Gauntlets = aktarget.GetWornForm(0x00000008) as Armor
			PlayerEquipmentBrsk.AddItem(Gauntlets, absilent=true)
			PlayerEquipmentBrsk.EquipItem(Gauntlets, true, true)
;       	Debug.MessageBox(Gauntlets)
		endif
;	check if a full head helmet is being worn or not, then add and equip it to the dummy if it is
		if (akTarget.GetWornForm(0x00000003) != None)
			Armor HeadHair = aktarget.GetWornForm(0x00000003) as Armor
			PlayerEquipmentBrsk.AddItem(HeadHair, absilent=true)
			PlayerEquipmentBrsk.EquipItem(HeadHair, true, true)
;   	else
;       	Debug.MessageBox("You have nothing equipped on HeadHair")
		endif
;	check if a head only helmet is being worn or not, then add and equip it to the dummy if it is
		if (akTarget.GetWornForm(0x00000001) != None) 
			Armor Head = aktarget.GetWornForm(0x00000001) as Armor
			PlayerEquipmentBrsk.AddItem(Head, absilent=true)
			PlayerEquipmentBrsk.EquipItem(Head, true, true)
		endif
;	check if a hair only helmet is being worn or not, then add and equip it to the dummy if it is
		if (akTarget.GetWornForm(0x00000002) != None) 
			Armor Hair = aktarget.GetWornForm(0x00000002) as Armor
			PlayerEquipmentBrsk.AddItem(Hair, absilent=true)
			PlayerEquipmentBrsk.EquipItem(Hair, true, true)
		endif
;   	if PlayerEquipmentBrsk.IsEquipped(Gauntlets)
;       	Debug.MessageBox(PlayerEquipmentBrsk + "has equipped" +  Gauntlets)
;   	else
;       	Debug.MessageBox(PlayerEquipmentBrsk + "could not equip" + Gauntlets)
;   	endif
;	This was to check if the NPC was equipping the stuff or not...
;   	PlayerEquipmentBrsk.Moveto(Game.GetPlayer()) 
		akTarget.EquipItem(ArmorBerserkerWBoots2, true, true)
		akTarget.EquipItem(ArmorBerserkerWCuirass2, true, true)
		akTarget.EquipItem(ArmorBerserkerGauntlets2, true, true)
		akTarget.EquipItem(ArmorBerserkWHelmet, true, true)
		aktarget.AddSpell(BerserkerArmorRevertForm)
		aktarget.RemoveSpell(BerserkerArmorBeastChange)
	else
		Dispel()
	endif
EndEvent

Event OnAnimationEvent(ObjectReference akSource, string asEventName)
;    Debug.Trace("VAMPIRE: Getting anim event -- " + akSource + " " + asEventName)
    if (asEventName == "SetRace")
        TransformIfNecessary(akSource as Actor)
    endif
EndEvent

Function TransformIfNecessary(Actor akTarget)
	if (akTarget == None)
		return
	endif
	UnRegisterForAnimationEvent(akTarget, "SetRace")
	akTarget.placeatme(FXVampChangeExplosion)
	if bRemoveGhost = 1
		akTarget.GetActorBase().SetInvulnerable(false)
		akTarget.SetGhost(false)
	endif
;	if abIsGhost == true
;		akTarget.SetGhost(false)	;it is possible that actor will lose ghost status even if his status is ghost by default
;	endif
EndFunction

Event OnEffectFinish(Actor akTarget, Actor akCaster)
	RevertForm(PlayerRef)
EndEvent

Function RevertForm(Actor akTarget)
    Deintegrate.Play(akTarget)
    utility.wait(2.0)
    akTarget.UnEquipItem(ArmorBerserkerWBoots2, false, true)
    akTarget.UnEquipItem(ArmorBerserkerWCuirass2, false, true)
    akTarget.UnEquipItem(ArmorBerserkerGauntlets2, false, true)
    akTarget.UnEquipItem(ArmorBerserkWHelmet, false, true)
    akTarget.EquipItem(ArmorBerserkerCuirass, false, true)
    akTarget.EquipItem(ArmorBerserkerBoots, false, true)
	;check if gauntlets are being worn or not
    if (PlayerEquipmentBrsk.GetWornForm(0x00000008) != None)
        Armor Gauntlets = PlayerEquipmentBrsk.GetWornForm(0x00000008) as Armor
        akTarget.EquipItem(Gauntlets, false, true)
    endif
    ;check if a full head helmet is being worn or not
    if (PlayerEquipmentBrsk.GetWornForm(0x00000003) != None)
        Armor HeadHair = PlayerEquipmentBrsk.GetWornForm(0x00000003) as Armor
        akTarget.EquipItem(HeadHair, false, true)
    endif
    ;check if a head only helmet is being worn or not
    if (PlayerEquipmentBrsk.GetWornForm(0x00000001) != None) 
        Armor Head = PlayerEquipmentBrsk.GetWornForm(0x00000001) as Armor
        akTarget.EquipItem(Head, false, true)
    endif
    ;check if a hair only helmet is being worn or not
    if (PlayerEquipmentBrsk.GetWornForm(0x00000002) != None) 
        Armor Hair = PlayerEquipmentBrsk.GetWornForm(0x00000002) as Armor
        akTarget.EquipItem(Hair, false, true)
    endif
    PlayerEquipmentBrsk.RemoveAllItems()
;Checking if it did remove them again
;   PlayerEquipmentBrsk.MoveTo(Game.GetPlayer())    
    aktarget.AddSpell(BerserkerArmorBeastChange)
    aktarget.EquipSpell(BerserkerArmorBeastChange, 2)
    Deintegrate.Stop(akTarget)
	aktarget.RemoveSpell(BerserkerArmorRevertForm)
    akTarget.RemoveItem(ArmorBerserkerWBoots, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkerWBoots2, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkerWCuirass, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkerWCuirass2, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkerGauntlets, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkerGauntlets2, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkSHelmet, abSilent=true)
    akTarget.RemoveItem(ArmorBerserkWHelmet, abSilent=true)
EndFunction

 

 

 

2. qlBerserk_RevertFormScript

This is the script to revert form. It's attached to a magic effect which will be triggered from Revert Form Spell. This spell is added after Player entered a Transformation state (see qlBerserk_TransformScript). This script calls RevertForm function from qlBerserk_TransformScript. It also force control Player when he/she is Losing Control.

 

 

Scriptname qlBerserk_RevertFormScript extends ActiveMagicEffect  
{Berserker Armor revert form}

Quest Property Berserk_Quest Auto
MagicEffect Property BerserkArmorBeastT Auto
MagicEffect Property BerserkArmorBeastTDanger Auto
qlBerserk_TransformScript Property BerserkArmorBeastT Auto
qlBerserk_TransformScript Property BerserkArmorBeastTDanger Auto

GlobalVariable Property Berserk_Moral_Transformed Auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
	Actor Player = Game.GetPlayer()
	If Player.HasMagicEffect(BerserkArmorBeastT)
	  (BerserkArmorBeastT as qlBerserk_TransformScript).RevertForm(Player)
	ElseIF Player.HasMagicEffect(BerserkArmorBeastTDanger)
	  (BerserkArmorBeastTDanger as qlBerserk_TransformScript).RevertForm(Player)
	ElseIf Berserk_Moral_Transformed.GetValueInt() == 1
	  (Berserk_Quest as qlBerserk_QuestScript).TransformBack()
	Else
	   Return
	EndIf
EndEvent

 

 

 

3. Other scripts which manage transformation conditions and related stuff:

 

 

qlMETransformManagerScript

(attached to the main transformation magic effect which will trigger either Normal or Dangerous transformation)

 

 

Scriptname qlMETransformManagerScript extends ActiveMagicEffect
{defines global values for Berserker Armor transformation magic effects to correctly run under their own conditions}
; https://forums.nexusmods.com/index.php?/topic/7285811-how-to-script-2-magic-effects-with-different-conditions-and-they-nullify-each-other/

; List of all Magic Effects need to be managed:
; BerserkerArmorBeastT (main transformation, nullify below) METransform 1
; BerserkerArmorBeastTDanger (secondary transformation, nullify above) METransform 2
; BerserkerDamageHealRate (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageMagickaRate (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageStaminaRate (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageHealth (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageMagicka (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageStamina (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDebuffLongTerm1 (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDebuffLongTerm2 (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDamageHealthDanger (casted when BerserkerArmorBeastTDanger is active) 
; BerserkerDebuffLongTerm1b (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1
; BerserkerDebuffLongTerm2b (casted after BerserkerArmorBeastT worn off) MEValueDefiner 1

; -- PROPERTIES -- 
GlobalVariable Property MEValueDefiner Auto				;its value defines what MEs are active
GlobalVariable Property METransform Auto				;to check which one of normal transformation and dangerous transformation is active


; -- EVENTs --
EVENT OnEffectStart(Actor akTarget, Actor akCaster)
	METransformCheck()
ENDEVENT

;EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
;    METransform.SetValue(0)			;player is no more transformed
;ENDEVENT


; -- FUNCTIONs --
;-----------------------
FUNCTION METransformCheck()
;-----------------------
IF MEValueDefiner.GetValueInt() == 0
   METransform.SetValue(1)			;player is transformed normally   
ENDIF

IF MEValueDefiner.GetValueInt() == 1
   METransform.SetValue(2)			;player is transformed dangerously
ENDIF
ENDFUNCTION

 

 

 

qlMEBerserkerTransformScript

(attached to Normal Transformation magic effect)

 

 

Scriptname qlMEBerserkerTransformScript extends ActiveMagicEffect
{defines global value for Magic Effect BerserkerArmorBeastT to active}
; https://forums.nexusmods.com/index.php?/topic/7285811-how-to-script-2-magic-effects-with-different-conditions-and-they-nullify-each-other/

; -- PROPERTIES -- 
GlobalVariable Property METransform Auto				;to check which one of normal transformation and dangerous transformation is active
GlobalVariable Property MEValueDefiner Auto				;its value defines what MEs are active

; -- EVENTs --
;EVENT OnEffectStart(Actor akTarget, Actor akCaster)
;IF METransform.GetValueInt() != 1
;   METransform.SetValue(1)			;player is transformed normally
;ENDIF
;ENDEVENT

EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
	MEValueDefiner.SetValue(1)		;set the value to activate damage attributes and debuffs
ENDEVENT

 

 

 

qlMEBerserkerTransformDangerScript

(attached to Dangerous Transformation magic effect)

 

 

Scriptname qlMEBerserkerTransformDangerScript extends ActiveMagicEffect
{defines global value for Magic Effect BerserkerArmorBeastTDanger to active}
; https://forums.nexusmods.com/index.php?/topic/7285811-how-to-script-2-magic-effects-with-different-conditions-and-they-nullify-each-other/

; -- PROPERTIES -- 
GlobalVariable Property METransform Auto				;to check which one of normal transformation and dangerous transformation is active
GlobalVariable Property MEValueDefiner Auto				;its value defines what MEs are active

; -- EVENTs --
;EVENT OnEffectStart(Actor akTarget, Actor akCaster)
;IF METransform.GetValueInt() != 2
;   METransform.SetValue(2)			;player is transformed dangerously
;ENDIF
;ENDEVENT

EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
	MEValueDefiner.SetValue(1)		;set the value to activate damage attributes and debuffs
ENDEVENT 

 

 

 

qlMENegativeEffectsScript

(attached to a Negative Effect magic effect)

 

 

Scriptname qlMENegativeEffectsScript extends ActiveMagicEffect
{defines global value for all negative magic effects to active}
; https://forums.nexusmods.com/index.php?/topic/7285811-how-to-script-2-magic-effects-with-different-conditions-and-they-nullify-each-other/

; -- PROPERTIES -- 
GlobalVariable Property METransform Auto				;to check which one of normal transformation and dangerous transformation is active
GlobalVariable Property MEValueDefiner Auto				;its value defines what MEs are active

; -- EVENTs --
;EVENT OnEffectStart(Actor akTarget, Actor akCaster)
;IF MEValueDefiner.GetValueInt() != 1
;   MEValueDefiner.SetValue(1)		;set the value to activate damage attributes and debuffs
;ENDIF
;ENDEVENT

EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
	MEValueDefiner.SetValue(0)		;set the value to deactivate damage attributes and debuffs
ENDEVENT 

 

 

 

 

Edited by qwertypol012
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...