saintgrimm92 Posted November 11, 2022 Share Posted November 11, 2022 I have a once/day power to summon a main NPC from my upcoming mod. He's a vampire lord and the summon summons him already in his vampire lord form.I've got him sets up as a mage and I've noticed when he's in his walking stance he'll equip spells, but not use them, just run around. But then when he goes into his floating stance, he starts casting them normally.So I need a way to force him to always float so he'll always be attacking targets instead of running around with spells equipped just doing nothing half the time.I had a look at the magic version of Harkon, and he just seems to have a script that gives him certain spells depending on the player's level, so I'm not sure where/how his stances are controlled. So I'm not sure where/how to control this NPC either.I'd assume this would be another combatstate==1 situation, but I have no idea what to call on to say "do the floating"Tried googling this one, but all I ended up finding were mods to give the vampire lord form actual flight.... So not much help there. Link to comment Share on other sites More sharing options...
Sphered Posted November 11, 2022 Share Posted November 11, 2022 So you basically want him to hover like a dragonpriest at all times This would be tied to behavior graphs. There is the graphvariable called "IsLevitating" for that race, but that is governed under the hood and it might require he meet other criteria to actually hover, but you can always set the variable manually and see if it makes it happen, but I have no idea if it will Whoever.SetAnimationVariableInt("IsLevitating",1) Otherwise you might need to manually replace animations Link to comment Share on other sites More sharing options...
saintgrimm92 Posted November 12, 2022 Author Share Posted November 12, 2022 Well lets hope the variable setting works then :P lol thank you! Link to comment Share on other sites More sharing options...
Sphered Posted November 12, 2022 Share Posted November 12, 2022 A decent reference of replacing animations would be nexusmods.com/skyrim/mods/71391 It enables the playable human races to use dragonpriest animations instead. Likely wont look right on vampire lord but it could help demonstrate how to replace animations. Theres also that mod dynamic animation replacer to do it for just one npc and affect no one else Its really just a matter of replacing hkx files but I know that can be overwhelming if you havent done it before. Best of luck Link to comment Share on other sites More sharing options...
maxarturo Posted November 12, 2022 Share Posted November 12, 2022 (edited) Except from the vanilla game Harkon boss fight and my own mod, I personally have never seen this done before in any mod I've played the last 12 years. And this is because the DLC1 vampire lord assets are completely broken, they are from the worst created assets by Bethesda, so in order to do the: - Transform to Vampire Lord > Vampire Lord Melee combat style > To Vampire Lord magic combat style (floating), and vise versa. 1) Except that you need 3 actors 2) You have to do this inside a controlled combat scene, because you can't change combat style on the vamipre lord on the fly using the same actor, it just won't work!! This means that this requires a specific setup that you will end up with more than 1 script containing, as with my circumstance, thousands of lines!!! To get an idea of how this 'change combat style' is done on the vampire lord, take a look at how Bethesda coded the whole thing. And you need to take in account that 'that' specific vanilla combat scene despite the coding, is completely bugged!!!! Even here they F***** Up everything!! Script name = DLC1dunHarkonBatTeleport This is the part where the change combat style is done, by actually changing actors and transfering their 'actor values'. Function SwapHarkons() ;;Debug.Trace("Swapping Harkons!") if (!initialized) initialized = True Initialize() EndIf HarkonBattleMagicFormActor.StopTranslation() HarkonBattleMeleeFormActor.StopTranslation() VQ08HarkonSwapMarker.MoveTo(SelfActor) if (SelfActor == HarkonBattleMagicFormActor) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonWarpMarker1) HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMagicFormActor, HarkonBattleMeleeFormActor) Self.ForceRefTo(HarkonBattleMeleeFormActor) OldActor = HarkonBattleMagicFormActor SelfActor = HarkonBattleMeleeFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMeleeFormActor ;;Debug.Trace("Melee Form, Go!") Else HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonWarpMarker2) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMeleeFormActor, HarkonBattleMagicFormActor) Self.ForceRefTo(HarkonBattleMagicFormActor) OldActor = HarkonBattleMeleeFormActor SelfActor = HarkonBattleMagicFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMagicFormActor ;;Debug.Trace("Magic Form, Go!") EndIf OldActor.StopCombat() SelfActor.SetSubGraphFloatVariable("fdampRate", 0.02) ;Fade in the new Harkon. SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0) ;;Debug.Trace("Swap complete. Self=" + Self.GetActorRef()) SelfActor.EvaluatePackage() EndFunction Function TransferAVs(Actor source, Actor destination) ;Debug.Trace("IN:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) if (destination.GetAV("Health") > source.GetAV("Health")) destination.DamageAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) Else destination.RestoreAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) EndIf ;Debug.Trace("Outgoing Health: " + source.GetAV("Health") + " == " + destination.GetAV("Health")) if (destination.GetAV("Magicka") > source.GetAV("Magicka")) destination.DamageAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) Else destination.RestoreAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) EndIf if (destination.GetAV("Stamina") > source.GetAV("Stamina")) destination.DamageAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) Else destination.RestoreAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) EndIf ;Debug.Trace("OUT:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) destination.SetAV("Variable06", 1) ;*Always* 1 at this point, since we're in the middle of a teleport. destination.SetAV("Variable07", source.GetAV("Variable07")) destination.SetAV("Variable10", source.GetAV("Variable10")) EndFunction This is the entire vanilla script handeling the actor: ScriptName DLC1dunHarkonBatTeleport extends ReferenceAlias {Script for Harkon's bat-based teleporting.} ;References for swapping between Harkon's Melee and Magic forms (which are actually different actors!). ReferenceAlias property HarkonBattleMainForm Auto ReferenceAlias property HarkonBattleMagicForm Auto ReferenceAlias property HarkonBattleMeleeForm Auto Actor property SelfActor Auto Hidden Actor property OldActor Auto Hidden Actor property HarkonBattleRealHarkonActor Auto Hidden Actor property HarkonBattleMeleeFormActor Auto Hidden Actor property HarkonBattleMagicFormActor Auto Hidden bool initialized ObjectReference property VQ08HarkonSwapMarker Auto ObjectReference property VQ08HarkonWarpMarker1 Auto ObjectReference property VQ08HarkonWarpMarker2 Auto ObjectReference property teleportObject auto hidden ;The location we're teleporting to. Spell property DLC1VQ08Bats auto ;Bat spell that handles the actual teleport. Spell property DLC1VQ08SwapBats auto ;Bat spell that handles the actual teleport. bool property swappingHarkons auto hidden ;Are we swapping Harkons on this teleport? ObjectReference property newHarkon auto Hidden ;If so, who are we swapping to? Function Initialize() SelfActor = Self.GetActorRef() HarkonBattleMeleeFormActor = HarkonBattleMeleeForm.GetActorRef() HarkonBattleMagicFormActor = HarkonBattleMagicForm.GetActorRef() EndFunction Function BatTeleportTo(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf teleportObject = obj swappingHarkons = False ;;Debug.Trace("Begin Harkon Teleport to " + teleportObject) ;;SelfActor.GetActorBase().SetInvulnerable(True) SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() SelfActor.DispelAllSpells() ;;Debug.Trace("Attempting teleport.") SelfActor.DoCombatSpellApply(DLC1VQ08Bats, Self.GetActorRef()) EndFunction Function BatTeleportAndSwapTo(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf teleportObject = obj swappingHarkons = True if (SelfActor == HarkonBattleMagicFormActor) newHarkon = HarkonBattleMeleeFormActor Else newHarkon = HarkonBattleMagicFormActor EndIf ;;Debug.Trace("Begin Harkon Swap Teleport to " + teleportObject) ;;SelfActor.GetActorBase().SetInvulnerable(True) HarkonBattleMagicFormActor.SetAV("Variable06", 1) HarkonBattleMagicFormActor.EvaluatePackage() HarkonBattleMagicFormActor.DispelAllSpells() ;HarkonBattleMagicFormActor.SetSubGraphFloatVariable("fdampRate", 0.20) ;Animation fadeout. ;HarkonBattleMagicFormActor.SetSubGraphFloatVariable("ftoggleBlend", 1.3) HarkonBattleMeleeFormActor.SetAV("Variable06", 1) HarkonBattleMeleeFormActor.EvaluatePackage() HarkonBattleMeleeFormActor.DispelAllSpells() ;HarkonBattleMeleeFormActor.SetSubGraphFloatVariable("fdampRate", 0.20) ;Animation fadeout. ;HarkonBattleMeleeFormActor.SetSubGraphFloatVariable("ftoggleBlend", 1.3) SelfActor.DoCombatSpellApply(DLC1VQ08SwapBats, newHarkon) EndFunction Function BatTeleportToEndMagic(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf if (SelfActor == HarkonBattleMagicFormActor) BatTeleportTo(obj) Else BatTeleportAndSwapTo(obj) EndIf EndFunction Function BatTeleportToEndMelee(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf if (SelfActor == HarkonBattleMeleeFormActor) BatTeleportTo(obj) Else BatTeleportAndSwapTo(obj) EndIf EndFunction Function SwapHarkons() ;;Debug.Trace("Swapping Harkons!") if (!initialized) initialized = True Initialize() EndIf HarkonBattleMagicFormActor.StopTranslation() HarkonBattleMeleeFormActor.StopTranslation() VQ08HarkonSwapMarker.MoveTo(SelfActor) if (SelfActor == HarkonBattleMagicFormActor) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonWarpMarker1) HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMagicFormActor, HarkonBattleMeleeFormActor) Self.ForceRefTo(HarkonBattleMeleeFormActor) OldActor = HarkonBattleMagicFormActor SelfActor = HarkonBattleMeleeFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMeleeFormActor ;;Debug.Trace("Melee Form, Go!") Else HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonWarpMarker2) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMeleeFormActor, HarkonBattleMagicFormActor) Self.ForceRefTo(HarkonBattleMagicFormActor) OldActor = HarkonBattleMeleeFormActor SelfActor = HarkonBattleMagicFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMagicFormActor ;;Debug.Trace("Magic Form, Go!") EndIf OldActor.StopCombat() SelfActor.SetSubGraphFloatVariable("fdampRate", 0.02) ;Fade in the new Harkon. SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0) ;;Debug.Trace("Swap complete. Self=" + Self.GetActorRef()) SelfActor.EvaluatePackage() EndFunction Function TransferAVs(Actor source, Actor destination) ;Debug.Trace("IN:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) if (destination.GetAV("Health") > source.GetAV("Health")) destination.DamageAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) Else destination.RestoreAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) EndIf ;Debug.Trace("Outgoing Health: " + source.GetAV("Health") + " == " + destination.GetAV("Health")) if (destination.GetAV("Magicka") > source.GetAV("Magicka")) destination.DamageAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) Else destination.RestoreAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) EndIf if (destination.GetAV("Stamina") > source.GetAV("Stamina")) destination.DamageAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) Else destination.RestoreAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) EndIf ;Debug.Trace("OUT:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) destination.SetAV("Variable06", 1) ;*Always* 1 at this point, since we're in the middle of a teleport. destination.SetAV("Variable07", source.GetAV("Variable07")) destination.SetAV("Variable10", source.GetAV("Variable10")) EndFunction Function BatsAllDone() if (swappingHarkons) SwapHarkons() EndIf if (SelfActor == HarkonBattleMagicFormActor) (SelfActor as DLC1HarkonCombatMagicLevelingScript).ReequipDrainSpell() EndIf SelfActor.SetAV("Variable06", 1) ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).HarkonReforms() EndFunction And this is the vanilla combat script inside the quest responsible for everything: ScriptName DLC1dunHarkonBossBattle extends ReferenceAlias {The main boss battle script for Harkon at the end of VQ08.} ;Harkon Actor Variables: ; - Variable06 is used as a package condition to control his combat state. ; 1 = Stay in Place (Wait for Teleport, Dying, Toggle, etc.) ; 2 = Shrine Event spellcasting ; 3-5 = Mistform Patrols ;Float that controls the state of the battle. Used to be an int, but I needed more granularity. ; 0-1 = Normal Combat ; 0 = Default Magic Combat. We stay in this for 60s or until a Shrine Event triggers. Teleports are to random positions. ; 0.25 = Default Melee Combat. We stay in this for 30s or until a Shrine Event triggers. Teleports are to nearby positions based on distance checks. ; 0.5 = Quick-Flip Combat. We may swap between magic and melee on each teleport. Teleports are determined by the form chosen. ; 1-3 = Starting Shrine Event 1-3 ; 4 = In Shrine Event ; 5 = In Mistform float property HarkonBossBattleState = 0.0 Auto Hidden ;VQ08 Quest this is an alias on. Quest property DLC1VQ08 Auto ReferenceAlias property HarkonBattleRealHarkon Auto ;Alias for the questline's Harkon. ReferenceAlias property HarkonBattleMeleeForm Auto ;Melee Form Harkon for this battle. ReferenceAlias property HarkonBattleMagicForm Auto ;Magic Form Harkon for this battle. Actor property SelfActor Auto Hidden Actor property HarkonBattleRealHarkonActor Auto Hidden Actor property HarkonBattleMeleeFormActor Auto Hidden Actor property HarkonBattleMagicFormActor Auto Hidden ;Whether the script has run its initialization (occurs on combat start). bool scriptVariablesInitialized bool initialized ;Unique spells manipulated by this script. Spell property DLC1dunHarkonConjureGargoyleLeftHand Auto Spell property DLC1HarkonDrain02Alt Auto Spell property DLC1HarkonMistform Auto Spell property DLC1dunHarkonInvulnerabilityShield Auto Spell property DLC1AbHarkonFloatBodyFX Auto bool SummonedInitialGargoyle ;Once Harkon summons a gargoyle or a shrine event triggers, we remove the spell. ;MAGIC/MELEE FORMS ReferenceAlias property HarkonBattleHoldPositionMarker Auto ;Where Magic Form Harkon is trying to get to. Effectively keeps him from running away all the time. ObjectReference property HarkonBattleHoldPositionMarkerObj Auto Hidden float TimeOfLastFormSwap = 0.0 float MaxTimeInMagicForm = 60.0 float MaxTimeInMeleeForm = 30.0 ;SHRINE EVENT ObjectReference property VQ08ShrineEventMarker Auto ;Location Harkon teleports to (atop shrine collision) ObjectReference property VQ08ShrineEventCollision Auto ;Collision to enable when Harkon is on the shrine. ObjectReference property VQ08EnemyTrigger1 Auto ;Traplinkers that hold the enemies to be activated. ObjectReference property VQ08EnemyTrigger2 Auto ObjectReference property VQ08EnemyTrigger3 Auto ObjectReference NextEnemyTrigger float ShrineEventTimestamp ;When the last Shrine Event triggered. float MinTimeBetweenShrineEvents = 8.0 ;Restricts when Harkon will next use a Shrine Event. float ShrineThreshold01 = 0.7 ;Health Thresholds that trigger the shrine events. float ShrineThreshold02 = 0.4 float ShrineThreshold03 = 0.2 bool ShrineEventActive = False ;Is a Shrine Event active? bool property ShieldDestroyed = False Auto Hidden ;Property checked by the shield magic effect to determine whether the shield blows up or gets dismissed quietly. int property LastShrineEvent = 0 Auto Hidden ;What Shrine Event did we do most recently? float ShrineEventFailsafeTimer = 0.0 ;Timer that kicks Harkon out of his Shrine event. Weapon property DLC1AurielsBow Auto ;Bow and Arrows to look for. Explosion property DLC1AurielsBowExp01 Auto Explosion property ExplosionIllusionDark01 Auto Ammo property DLC1ElvenArrowBlessed Auto Ammo property DLC1ElvenArrowBlood Auto bool property PlayerHasAurielsBow Auto Hidden Scene property DLC1VQ08HarkonBattleScene Auto ;Scene telling the player to use the bow. float property ShrineEventSceneTimer = 0.0 Auto hidden ;When did we last play this scene? ;MISTFORM ObjectReference property VQ08MistformEventMarker Auto ;Marker to translate to at start, to make sure Harkon doesn't get stuck on the shrine. ReferenceAlias property HarkonBattleNoNameAlias Auto ;Alias with an override empty name, to avoid awkward activation text. float MistformTimer ;Timer for when we started mistform. float MaxTimeInMistform = 20.0 ;Max time we'll stay in mistform. ;HARKON DEATH SCENE Spell property DLC1FXCastVampireBleedSpell Auto Armor property DLC1VampireSkeletonFXArmor Auto ObjectReference property DeadHarkonWarpMarker Auto bool inDeathThroesTeleport = False ;Are we in the final 'death throes' teleport? bool doneDeathThroesTeleport = False ;Have we finished the final 'death throes' teleport? bool doneFinalDeath = False ;Have we begun executing Harkon's OnDying block? int DeathThroesTeleportsLeft = 2 Activator property DLC1dunHarkonAshPile Auto ObjectReference property DLC1dunHarkonDeathFXAct Auto Sound property AmbRumbleShake Auto EffectShader property DLC1SunFireImpactFXShader Auto Spell property DLC1dunHarkonDeathSpell Auto ObjectReference property VQ08ExplosionSourceMarker Auto Explosion property DLC1VampiresBaneExplosion Auto ObjectReference property VQ08HarkonGroundMarker Auto Explosion property HarkonDeathExplosion Auto ;TELEPORTING ObjectReference[] property HarkonTeleportPoints Auto ;Array of possible teleport locations. int MultiTeleportCount = 0 ;Number of times to teleport in sequence. bool TeleportInProgress ;Is a teleport in progress? If so, prevent anything else from teleporting him. float MagicFormHoldPositionTimestamp = -1.0 float HealthPercentAtLastTeleport = 1.0 float MagicFormHealthPercentLossAllowed = 0.20 float MeleeFormHealthPercentLossAllowed = 0.30 float TimeOfLastTeleport = 0.0 float MaxTimeBetweenTeleports = 30.0 bool shouldTeleportCornered = False Static property HarkonTeleportMarker Auto ;The unique base object for Harkon's teleport markers. ;Harkon minions. ObjectReference[] property HarkonMinions Auto ;Hit Timestamps float TimerPreviousHit01 = 0.0 ;Timestamps that record when the player hits Harkon with an attacked. float TimerPreviousHit02 = 0.0 ;Used to approximately detect when he's been 'cornered' so he can escape. float TimerPreviousHit03 = 0.0 float TimerPreviousHit04 = 0.0 float TimerPreviousHit05 = 0.0 float TimerPreviousHit06 = 0.0 float TimerOfRecord = 0.0 ;Timer used to determine when Harkon was cornered. ;Harkon's Cape Armor property DLC1VampireLordCape Auto ;Music for the boss battle. Needed to prevent the audio from cutting in and out of combat constantly. MusicType property MUSCombatBoss Auto ;Vampire and Werewolf beast forms. Race property VampireBeastRace Auto Race property WerewolfBeastRace Auto ;-------------------------------------------------------------- ;Main Battle Loops: OnHit and OnUpdate ;--------------------------------------- Function InitializeHarkonBattle() if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf EndFunction Event OnCombatStateChanged(Actor akTarget, int aeCombatState) if (!initialized) initialized = True if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime TimeOfLastTeleport = currentTime PlayerHasAurielsBow = (Game.GetPlayer().GetItemCount(DLC1AurielsBow) > 0) ;Debug.Trace("Start the music!") MUSCombatBoss.Add() GoToState("Ready") OnUpdate() EndIf EndEvent Function InitializeScriptVariables() float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime TimeOfLastTeleport = currentTime SelfActor = Self.GetActorRef() HarkonBattleRealHarkonActor = HarkonBattleRealHarkon.GetActorRef() HarkonBattleMeleeFormActor = HarkonBattleMeleeForm.GetActorRef() HarkonBattleMagicFormActor = HarkonBattleMagicForm.GetActorRef() HarkonBattleMeleeFormActor.GetActorBase().SetEssential(False) HarkonBattleMagicFormActor.GetActorBase().SetEssential(False) HarkonBattleMeleeFormActor.StartDeferredKill() HarkonBattleMagicFormActor.StartDeferredKill() HarkonBattleHoldPositionMarkerObj = HarkonBattleHoldPositionMarker.GetReference() scriptVariablesInitialized = True EndFunction ;Every second, we check to see if we need to update Harkon's state. Function OnUpdate() If (!inDeathThroesTeleport) ProcessOnUpdateOROnHitEvent(None, None) RegisterForSingleUpdate(1) EndIf EndFunction Auto State Ready ;On hit, check to see which state we're in and respond accordingly. Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ProcessOnUpdateOROnHitEvent(aggressor, weap) EndEvent ;We have to handle both types of events in this single function to prevent interleaving errors from asynchronus events. Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) GoToState("Busy") ;Debug.Trace("HARKON: Harkon Processing----------") ;Debug.Trace("HARKON: PROCESSING: " + SelfActor + ", Health: " + SelfActor.GetAV("Health")) int activeMinionsCount = CountActiveMinions() float healthPercentage = SelfActor.GetAVPercentage("Health") float currentTime = Utility.GetCurrentRealTime() ;Are we dead or dying? If so, don't do anything. If (!initialized) ;Debug.Trace("HARKON: Not Initialized Yet") ;Do nothing! ElseIf (HarkonBossBattleState == 4 && inDeathThroesTeleport) ;If we're in the process of dying, but have wound up on the Shrine instead, bail out. EndShrineEvent(None, None) ElseIf (inDeathThroesTeleport) ;Debug.Trace("HARKON: In Death Throes") ;Do nothing! ElseIf (SelfActor.GetAV("Health") <= 0) ;Debug.Trace("HARKON: Triggering Death Throes") ;Start the Death Throes process. This is the only way we can catch the actor's 'death' because Deferred Kill is on. BeginDeathThroes() ElseIf (HarkonBossBattleState < 1) If (!SelfActor.IsInCombat()) SelfActor.StartCombat(Game.GetPlayer()) EndIf ;If we've passed one of the shrine health thresholds, that's our top priority. If (healthPercentage < ShrineThreshold01 && !ShrineEventActive && LastShrineEvent == 0) ;Debug.Trace("HARKON: Process SetupShrineEvent1") SetupShrineEvent(1) ElseIf (healthPercentage < ShrineThreshold02 && !ShrineEventActive && LastShrineEvent == 1 && (currentTime - ShrineEventTimestamp) > MinTimeBetweenShrineEvents) ;Debug.Trace("HARKON: Process SetupShrineEvent2") SetupShrineEvent(2) ElseIf (healthPercentage < ShrineThreshold03 && !ShrineEventActive && LastShrineEvent == 2 && (currentTime - ShrineEventTimestamp) > MinTimeBetweenShrineEvents) ;Debug.Trace("HARKON: Process SetupShrineEvent3") SetupShrineEvent(3) ElseIf (HarkonBossBattleState == 0 && currentTime - TimeOfLastFormSwap > MaxTimeInMagicForm) ;Debug.Trace("HARKON: Magic Form timer exceeded. Swapping forms.") TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0.25 TeleportHarkon() ElseIf (HarkonBossBattleState == 0.25 && currentTime - TimeOfLastFormSwap > MaxTimeInMeleeForm) ;Debug.Trace("HARKON: Melee Form timer exceeded. Swapping forms.") TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0 TeleportHarkon() ElseIf (SelfActor == HarkonBattleMagicFormActor && SelfActor.GetDistance(HarkonBattleHoldPositionMarker.GetReference()) < 400 && MagicFormHoldPositionTimestamp == -1) ;Debug.Trace("HARKON: Reached Hold Position marker. Setting Timestamp.") MagicFormHoldPositionTimestamp = currentTime ElseIf (SelfActor == HarkonBattleMagicFormActor && MagicFormHoldPositionTimestamp > 0 && currentTime - MagicFormHoldPositionTimestamp > 5) ;Debug.Trace("HARKON: Selecting new Hold Position Marker.") PickNewHoldPositionTargetWithEVP() ElseIf (SelfActor == HarkonBattleMagicFormActor && healthPercentage < (HealthPercentAtLastTeleport - MagicFormHealthPercentLossAllowed)) ;Debug.Trace("HARKON: Now Teleporting: Health Threshold (Magic) met.") TeleportHarkon() ElseIf (SelfActor == HarkonBattleMeleeFormActor && healthPercentage < (HealthPercentAtLastTeleport - MeleeFormHealthPercentLossAllowed)) ;Debug.Trace("HARKON: Now Teleporting: Health Threshold (Melee) met.") TeleportHarkon() ElseIf (shouldTeleportCornered && currentTime - TimerOfRecord < 4) ;Debug.Trace("HARKON: Waiting until full 4s are up.") ElseIf (shouldTeleportCornered && currentTime - TimerOfRecord >= 4) ;Debug.Trace("HARKON: Now Teleporting: Player cornered Harkon.") TeleportHarkon() ElseIf (currentTime - TimeOfLastTeleport > MaxTimeBetweenTeleports) ;Debug.Trace("HARKON: Now Teleporting: Max Time Between Teleports exceeded.") TeleportHarkon() ;Is Harkon in normal combat? If so, track hits so we can catch when he becomes 'stuck'. ElseIf (aggressor == Game.GetPlayer()) ;Debug.Trace("HARKON: Registered normal hit.") TimerPreviousHit06 = TimerPreviousHit05 TimerPreviousHit05 = TimerPreviousHit04 TimerPreviousHit04 = TimerPreviousHit03 TimerPreviousHit03 = TimerPreviousHit02 TimerPreviousHit02 = TimerPreviousHit01 TimerPreviousHit01 = currentTime if (SelfActor == HarkonBattleMagicFormActor && TimerPreviousHit01 - TimerPreviousHit04 < 6) shouldTeleportCornered = True TimerOfRecord = TimerPreviousHit04 ElseIf (SelfActor == HarkonBattleMeleeFormActor && TimerPreviousHit01 - TimerPreviousHit06 < 8) shouldTeleportCornered = True TimerOfRecord = TimerPreviousHit06 EndIf EndIf ElseIf (HarkonBossBattleState == 4) If (inDeathThroesTeleport) EndShrineEvent(None, None) ElseIf (activeMinionsCount == 0 && ShrineEventFailsafeTimer == 0) ;Debug.Trace("HARKON: Shrine Update: Setting Failsafe Timer") ShrineEventFailsafeTimer = currentTime ElseIf (activeMinionsCount > 0 && ShrineEventFailsafeTimer > 0) ;Debug.Trace("HARKON: Shrine Update: Jumped the gun. Resetting Shrine Event Failsafe Timer") ShrineEventFailsafeTimer = 0 ElseIf (activeMinionsCount == 0 && currentTime - ShrineEventFailsafeTimer >= 12) ;Debug.Trace("HARKON: Shrine Update: Failsafe Triggered") EndShrineEvent(None, None) ElseIf (ShrineEventSceneTimer == 0) ;Debug.Trace("HARKON: First time only, delay the scene.") ShrineEventSceneTimer = currentTime ElseIf (currentTime - ShrineEventSceneTimer > 15 && !DLC1VQ08HarkonBattleScene.IsPlaying() && PlayerHasAurielsBow) Race playerRace = Game.GetPlayer().GetRace() if ((VampireBeastRace == None || VampireBeastRace != playerRace) && (WerewolfBeastRace == None || WerewolfBeastRace != playerRace)) if (SelfActor.GetAV("Variable09") == 0) SelfActor.SetAV("Variable09", 1) EndIf ShrineEventSceneTimer = currentTime DLC1VQ08HarkonBattleScene.Start() EndIf EndIf ElseIf (HarkonBossBattleState == 5) ;If we're in Mistform, bail out if all of our minions are dead, or if time elapses. ;Debug.Trace("HARKON: Mistform Time: " + currentTime + ", " + MistformTimer) If (activeMinionsCount == 0) ;Debug.Trace("HARKON: Mistform Update: All minions dead.") EndMistform() ElseIf (currentTime > MistformTimer) ;Debug.Trace("HARKON: Mistform Update: Ending mistform by timer. " + Utility.GetCurrentRealTime() + " > " + MistformTimer) EndMistform() EndIf EndIf ;Debug.Trace("HARKON: ----------------------------") GoToState("Ready") EndFunction EndState State Busy Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ;Do Nothing EndEvent Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) ;Do Nothing EndFunction EndState Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ;Do nothing. EndEvent Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) ;Do Nothing EndFunction Function ProcessShadowShieldHit(ObjectReference aggressor, Form weap) ;This should all be handled by DLC1dunHarkonShadowShield now. ; ;If (HarkonBossBattleState == 4 && weap == DLC1AurielsBow) ; EndShrineEvent(aggressor as Actor, weap as Weapon) ;ElseIf (HarkonBossBattleState == 4 && weap != None && !DLC1VQ08HarkonBattleScene.IsPlaying() && Utility.GetCurrentRealTime() - ShrineEventSceneTimer > 5) ; ShrineEventSceneTimer = Utility.GetCurrentRealTime() ; DLC1VQ08HarkonBattleScene.Start() ;EndIf EndFunction ;Count how many 'active' enemies are left other than Harkon himself. int Function CountActiveMinions() int count = 0 int i = 0 Actor currentMinion While (i < HarkonMinions.Length) currentMinion = HarkonMinions[i] as Actor if (currentMinion.GetAV("Aggression") > 0 && !currentMinion.IsCommandedActor() && !currentMinion.IsDead()) count = count + 1 EndIf i = i + 1 EndWhile ;Debug.Trace("HARKON: Active Minions = " + count) return count EndFunction Function RecordSummonedGargoyle() if (!SummonedInitialGargoyle) ;Debug.Trace("HARKON: Gargoyle summoned. Removing the spell.") SummonedInitialGargoyle = True (SelfActor as DLC1HarkonCombatMagicLevelingScript).ReequipDrainSpell() EndIf SelfActor.RemoveSpell(DLC1dunHarkonConjureGargoyleLeftHand) EndFunction Function ResetHitClock() TimerPreviousHit01 = 0 TimerPreviousHit02 = 0 TimerPreviousHit03 = 0 TimerPreviousHit04 = 0 TimerPreviousHit05 = 0 TimerPreviousHit06 = 0 TimerOfRecord = 0 EndFunction ;-------------------------------------------------------------- ;Melee and Magic Forms ;--------------------- ;Harkon's Magic Form moves between Hold Position targets to allow him to move while still forcing him to stop periodically. Function PickNewHoldPositionTargetWithEVP() if (HarkonBossBattleState < 1 && !inDeathThroesTeleport) MagicFormHoldPositionTimestamp = -1 HarkonBattleHoldPositionMarker.ForceRefTo(PickTeleportMarkerRandomly()) if (SelfActor.GetAV("Variable06") <= 1) SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() EndIf if (SelfActor.GetAV("Variable06") <= 1) SelfActor.SetAV("Variable06", 0) SelfActor.EvaluatePackage() EndIf ;Debug.Trace("HARKON: PickNewHoldPositionTarget w/ EVPs resets Variable06 to 0.") EndIf EndFunction Function PickNewHoldPositionTargetWithoutEVP() MagicFormHoldPositionTimestamp = -1 HarkonBattleHoldPositionMarker.ForceRefTo(PickTeleportMarkerRandomly()) ;Debug.Trace("HARKON: PickNewHoldPositionTarget w/o EVPs picked a new target") EndFunction ;-------------------------------------------------------------- ;Teleport via Bats ;----------------- ;TRIGGERS: As Needed. Function TeleportHarkon() if (!TeleportInProgress && HarkonBossBattleState != 4) TeleportInProgress = True SelfActor.DispelAllSpells() ;Debug.Trace("HARKON: Now Teleporting. State: " + HarkonBossBattleState) if (inDeathThroesTeleport) ;Support legacy saves that had a higher number... if (DeathThroesTeleportsLeft > 2) DeathThroesTeleportsLeft = 2 EndIf if (DeathThroesTeleportsLeft == 2) BatTeleportToEndSameForm(HarkonTeleportPoints[11]) ElseIf (DeathThroesTeleportsLeft == 1) BatTeleportToEndSameForm(HarkonTeleportPoints[1]) ElseIf (DeathThroesTeleportsLeft == 0) BatTeleportToEndSameForm(HarkonTeleportPoints[0]) EndIf ElseIf (HarkonBossBattleState == 0) ;Standard magic teleport. BatTeleportToEndMagic(PickTeleportMarkerRandomly()) ElseIf (HarkonBossBattleState == 0.25) ;Standard melee teleport. BatTeleportToEndMelee(PickTeleportMarkerNearby()) ElseIf (HarkonBossBattleState == 0.5) int roll = Utility.RandomInt(0, 1) if (roll == 0) ;Debug.Trace("HARKON: Magic!") BatTeleportToEndMagic(PickTeleportMarkerRandomly()) Else ;Debug.Trace("HARKON: Melee!") BatTeleportToEndMelee(PickTeleportMarkerNearby()) EndIf Else ;Debug.Trace("HARKON: TeleportHarkon() called in invalid state: " + HarkonBossBattleState) EndIf EndIf EndFunction ObjectReference Function PickTeleportMarkerRandomly() int point = Utility.RandomInt(0, HarkonTeleportPoints.Length - 1) ;Debug.Trace("HARKON: Pick Randomly: " + point + ": " + HarkonTeleportPoints[point]) return HarkonTeleportPoints[point] EndFunction ObjectReference Function PickTeleportMarkerNearby() ObjectReference closestMarker = Game.FindClosestReferenceOfTypeFromRef(HarkonTeleportMarker, Game.GetPlayer(), 3000) if (closestMarker == None) closestMarker = PickTeleportMarkerRandomly() EndIf ;Identify the closest teleport point, offset from it +/-2, and go there. int closestPoint = 0 int i = 0 While (i < HarkonTeleportPoints.Length) if (HarkonTeleportPoints[i] == closestMarker) closestPoint = i i = HarkonTeleportPoints.Length EndIf i = i + 1 EndWhile int offset = Utility.RandomInt(0, 1) if (offset == 0) offset = -1 EndIf int targetPoint = closestPoint + offset if (targetPoint >= HarkonTeleportPoints.Length) targetPoint = 0 ElseIf (targetPoint < 0) targetPoint = HarkonTeleportPoints.Length - 1 EndIf return HarkonTeleportPoints[targetPoint] EndFunction Function BatTeleportToEndSameForm(ObjectReference target) if (SelfActor == HarkonBattleMagicFormActor) BatTeleportToEndMagic(target) Else BatTeleportToEndMelee(target) EndIf EndFunction Function BatTeleportToEndSwapForm(ObjectReference target) if (SelfActor == HarkonBattleMagicFormActor) BatTeleportToEndMelee(target) Else BatTeleportToEndMagic(target) EndIf EndFunction Function BatTeleportToEndMagic(ObjectReference target) ;Debug.Trace("HARKON: BatTeleportToEndMagic called.") ;Debug.Trace("HARKON: Updating health thresholds.") HealthPercentAtLastTeleport = SelfActor.GetAVPercentage("Health") TimeOfLastTeleport = Utility.GetCurrentRealTime() PickNewHoldPositionTargetWithoutEVP() TimerOfRecord = 0 shouldTeleportCornered = False ((Self as ReferenceAlias) as DLC1dunHarkonBatTeleport).BatTeleportToEndMagic(target) EndFunction Function BatTeleportToEndMelee(ObjectReference target) ;Debug.Trace("HARKON: BatTeleportToEndMelee called.") ;Debug.Trace("HARKON: Updating health thresholds.") HealthPercentAtLastTeleport = SelfActor.GetAVPercentage("Health") TimeOfLastTeleport = Utility.GetCurrentRealTime() TimerOfRecord = 0 shouldTeleportCornered = False ((Self as ReferenceAlias) as DLC1dunHarkonBatTeleport).BatTeleportToEndMelee(target) EndFunction Function HarkonReforms() ;Harkon is reforming from bat form. Determine what, if anything, we want to do in response. ;Debug.Trace("HARKON: Harkon Reformed.") HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) ;Make sure we don't stay in the teleport package. if (HarkonBossBattleState != 4 && !inDeathThroesTeleport) SelfActor.SetAV("Variable06", 0) EndIf ;Allow teleports going forward. TeleportInProgress = False ResetHitClock() If (HarkonBossBattleState == 1 || HarkonBossBattleState == 2 || HarkonBossBattleState == 3) ;Debug.Trace("HARKON: Harkon Starts Shrine") StartShrineEvent() ElseIf (inDeathThroesTeleport) if (SelfActor == HarkonBattleMagicFormActor) ;Debug.Trace("HARKON: Moving Melee Harkon to platform.") HarkonBattleMeleeFormActor.MoveTo(DeadHarkonWarpMarker) Else ;Debug.Trace("HARKON: Moving Magic Harkon to platform.") HarkonBattleMagicFormActor.MoveTo(DeadHarkonWarpMarker) EndIf if (SelfActor.GetAV("Variable10") <= 0) SelfActor.SetAV("Variable10", 1) Else SelfActor.SetAV("Variable10", 0) EndIf if (DeathThroesTeleportsLeft > 0) DeathThroesTeleportsLeft = DeathThroesTeleportsLeft - 1 SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() TeleportHarkon() Else ;Debug.Trace("HARKON: Harkon Reformed. Kill him.") doneDeathThroesTeleport = True SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() EndDeathThroes() EndIf ElseIf (MultiTeleportCount > 0) MultiTeleportCount = MultiTeleportCount - 1 TeleportHarkon() Else SelfActor.StartCombat(Game.GetPlayer()) EndIf SelfActor.EvaluatePackage() EndFunction ;-------------------------------------------------------------- ;Shrine Events ;--------------- ;TRIGGERS: Health Thresholds. ;Trigger enemies and warp Harkon to the Shrine. Function SetupShrineEvent(int eventNum) If (TeleportInProgress) Return EndIf SelfActor.DispelAllSpells() TeleportInProgress = True ShrineEventActive = True if (eventNum == 1) NextEnemyTrigger = VQ08EnemyTrigger1 ;Strip the Summon Gargoyle spell, if it hasn't been used already. RecordSummonedGargoyle() ElseIf (eventNum == 2) NextEnemyTrigger = VQ08EnemyTrigger2 Else NextEnemyTrigger = VQ08EnemyTrigger3 EndIf HarkonBossBattleState = eventNum LastShrineEvent = eventNum VQ08ShrineEventCollision.Enable() BatTeleportToEndMagic(VQ08ShrineEventMarker) EndFunction ;Activate Harkon's shield spell. Function StartShrineEvent() ;Update Harkon's AI so he stays put and casts his invulnerability shield spell. if (SelfActor.GetAV("Health") < 0) if (inDeathThroesTeleport) HarkonBossBattleState = 0 TeleportHarkon() Else BeginDeathThroes() EndIf Return EndIf SelfActor.EquipSpell(DLC1dunHarkonInvulnerabilityShield, 1) SelfActor.SetAV("Variable06", 2) SelfActor.EvaluatePackage() NextEnemyTrigger.Activate(SelfActor) if (LastShrineEvent > 1) ShrineEventSceneTimer = Utility.GetCurrentRealTime() EndIf HarkonBossBattleState = 4 EndFunction ;Cancel shield spell and complete shrine event. Function EndShrineEvent(Actor aggressorIfAny, Weapon weaponIfAny) if (HarkonBossBattleState == 4) ;Clean up the Shrine event. ;Debug.Trace("HARKON: Shrine Event is ending. Clean up.") float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0 if (weaponIfAny == DLC1AurielsBow) ShieldDestroyed = True SelfActor.RampRumble(0.75, 2, 1600) Game.ShakeCamera(SelfActor, 0.75, 2) Else ShieldDestroyed = False EndIf SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() SelfActor.InterruptCast() SelfActor.DispelSpell(DLC1dunHarkonInvulnerabilityShield) HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) if (weaponIfAny == DLC1AurielsBow) if (AggressorIfAny.IsEquipped(DLC1ElvenArrowBlessed)) ;Debug.Trace("HARKON: Sunhallowed Arrow: Big explosion! Big damage!") ;SelfActor.PlaceAtMe(DLC1AurielsBowExp01) DealShrineEventHealthDamage(50) ElseIf (!AggressorIfAny.IsEquipped(DLC1ElvenArrowBlood)) ;Debug.Trace("HARKON: Normal Arrow: Explosion! Damage! Stagger!") ;SelfActor.PlaceAtMe(DLC1AurielsBowExp01) DealShrineEventHealthDamage(25) Else ;Debug.Trace("HARKON: Blood Arrow: Small explosion. Just End It!") ;SelfActor.PlaceAtMe(ExplosionIllusionDark01) EndIf Else ;("Shrine event ended normally.") EndIf ShrineEventActive = False ShrineEventTimestamp = currentTime ShrineEventFailsafeTimer = 0 ;Debug.Trace("HARKON: Evaluating next step. " + LastShrineEvent + ", " + CountActiveMinions()) if (inDeathThroesTeleport) TeleportHarkon() ElseIf (LastShrineEvent == 2 && CountActiveMinions() > 0) StartMistform() ElseIf ((LastShrineEvent == 1 || LastShrineEvent == 3) && CountActiveMinions() > 1) StartMistform() Else SelectPostShrineCombatStage() TeleportHarkon() EndIf VQ08ShrineEventCollision.Disable() EndIf EndFunction Function SelectPostShrineCombatStage() if (LastShrineEvent < 2) HarkonBossBattleState = 0 ElseIf (LastShrineEvent == 2) HarkonBossBattleState = 0.25 Else HarkonBossBattleState = 0.5 EndIf EndFunction Function DealShrineEventHealthDamage(int damage) if (SelfActor.GetAV("Health") > damage) SelfActor.DamageAV("Health", damage) Else SelfActor.DamageAV("Health", (SelfActor.GetAV("Health") - 1)) EndIf EndFunction ;-------------------------------------------------------------- ;Mistform Events ;--------------- ;TRIGGERS: After a Shrine Event, if 2+ enemies are active. Function StartMistform() ;Begin running a mistform patrol package. int patrol = Utility.RandomInt(0, 1) SelfActor.SetAV("Variable06", (3 + patrol)) SelfActor.EvaluatePackage() ;Cast Mistform and hide Harkon's name so he can't be selected. DLC1HarkonMistform.Cast(SelfActor, SelfActor) SelfActor.TranslateToRef(VQ08MistformEventMarker, 6000.0, 0.0) HarkonBattleNoNameAlias.ForceRefTo(SelfActor) ;Set timestamp and battle state. MistformTimer = Utility.GetCurrentRealTime() + MaxTimeInMistform HarkonBossBattleState = 5 ;Debug.Trace("HARKON: Harkon entered mistform and started patrol " + (3+patrol) + " with timer " + MistformTimer) EndFunction Function EndMistform() TimeOfLastFormSwap = Utility.GetCurrentRealTime() SelectPostShrineCombatStage() SelfActor.DispelSpell(DLC1HarkonMistform) HarkonBattleNoNameAlias.Clear() HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) SelfActor.SetAV("Variable06", 0) SelfActor.StartCombat(Game.GetPlayer()) SelfActor.EvaluatePackage() ;Debug.Trace("HARKON: Harkon exited mistform.") EndFunction ;-------------------------------------------------------------- ;Death Events ;------------ Event OnDeath(Actor akKiller) if (!doneFinalDeath) Debug.Trace("ERROR: HARKON HAS DIED PREMATURELY.") doneDeathThroesTeleport = True EndDeathThroes() EndIf EndEvent Function BeginDeathThroes() if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf if (!inDeathThroesTeleport) inDeathThroesTeleport = True SelfActor.BlockActivation() SelfActor.GetActorBase().SetInvulnerable(True) ;Kill any surviving minions. int i = 0 While (i < HarkonMinions.Length) if ((HarkonMinions[i] as DLC1dunHarkonReanimatedMinions) != None) (HarkonMinions[i] as DLC1dunHarkonReanimatedMinions).IsSpecialDeath = True EndIf if (!HarkonMinions[i].IsDisabled()) (HarkonMinions[i] as Actor).Kill() EndIf i = i + 1 EndWhile TeleportHarkon() EndIf EndFunction Event EndDeathThroes() If (doneDeathThroesTeleport && !doneFinalDeath) ;Debug.Trace("HARKON: End death teleport. Harkon killed by script.") ;Begin disintegration and vfx. doneFinalDeath = True MUSCombatBoss.Remove() SelfActor.SetSubGraphFloatVariable("fdampRate", 1.0) ;;speeds up fade rate (max 1 min .1 SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0);;blends between two anims default 0 (0 = there 1 = gone) DLC1FXCastVampireBleedSpell.Cast(SelfActor, SelfActor) VQ08HarkonGroundMarker.MoveTo(SelfActor, 0, 0, -1, False) DLC1dunHarkonDeathFXAct.MoveTo(VQ08HarkonGroundMarker) DLC1dunHarkonDeathFXAct.PlayAnimation("PlayAnim01") SelfActor.BlockActivation(True) SelfActor.SetGhost(True) SelfActor.ClearExtraArrows() ;This is almost certainly unnecessary, but just to be safe, we'll end the deferred kill state for both actors, then make sure the current one will die. HarkonBattleMagicFormActor.EndDeferredKill() HarkonBattleMeleeFormActor.EndDeferredKill() ;SelfActor.Kill() SelfActor.SetCriticalStage(SelfActor.CritStage_DisintegrateStart) Utility.Wait(1) AmbRumbleShake.Play(VQ08HarkonGroundMarker) Game.GetPlayer().RampRumble(1, 2, 1600) Game.ShakeCamera(SelfActor, 1, 2) SelfActor.PlaceAtMe(HarkonDeathExplosion) ;Pull in the 'Real' Harkon and make him the ash pile, so the player doesn't lose any items ;they may have reverse-pickpocketed onto him during the questline. ;Wait for Harkon to collapse. Utility.Wait(4) HarkonBattleRealHarkonActor.GetActorBase().SetEssential(False) HarkonBattleRealHarkonActor.Kill() HarkonBattleRealHarkonActor.SetCriticalStage(HarkonBattleRealHarkonActor.CritStage_DisintegrateStart) HarkonBattleRealHarkonActor.MoveTo(VQ08HarkonGroundMarker) HarkonBattleRealHarkonActor.AttachAshPile(DLC1dunHarkonAshPile) HarkonBattleRealHarkonActor.SetCriticalStage(HarkonBattleRealHarkonActor.CritStage_DisintegrateEnd) HarkonBattleRealHarkonActor.Enable() Utility.Wait(10) ;Finish dissolving Harkon. SelfActor.SetCriticalStage(SelfActor.CritStage_DisintegrateEnd) ;Complete and shut down quest. DLC1VQ08.SetStage(200) EndIf EndEvent And here is my approach to change combat style / change actors which is done in my mod, I'm only posting the function that swamp actors, and not the whole script: * The obtain 'actor's AV' and store it, is done by another function not living inside this one. Here is a 'fixed' function that serves as a 'Fail Safe' inside the script, just in case something went wrong with the player's playthrough this will fire instead of the more demanding function. FUNCTION SwapActor() If ( IsMelee == True ) SearchGoal() If ( FoundTele == True ) ObjectReference BatFX01 = Self.PlaceAtMe(XMDBatsFXActivator01, 1) Wait(0.5) Self.DisableNoWait() ActorMagic.Enable() ActorMagic.DamageActorValue("Health", ActorMagic.GetActorValue("Health") * 0.50) ActorMagic.TranslateToRef(TeleportGoal, 16000.0, 0.0) DLC1VampireBatsVFX.Play(ActorMagic, 2) While ( ActorReady == False ) If ( ActorMagic.IsDisabled() == False ) && ( ActorMagic.getDistance(TeleportGoal) <= 512 ) ActorReady = True Else ActorMagic.Enable() ActorMagic.TranslateToRef(TeleportGoal, 16000.0, 0.0) DLC1VampireBatsVFX.Play(ActorMagic, 2) EndIf EndWhile Wait(0.5) BatFX01.TranslateToRef(teleportGoal, 500, 0.0) ActorMagic.StartCombat(opponent as actor) Self.RemoveFromFaction(XMD_XMVampireFaction) Self.AddToFaction(XMDvampLegionFriendlyFaction) Self.SetActorValue("Aggression", 0) Self.MoveTo(XMDMeleeSwapHeading) BatFX01.Delete() BatFX01 = None Else SearchGoal() EndIf Else ObjectReference BatFX02 = Self.PlaceAtMe(XMDBatsFXActivator01, 1) Wait(0.5) Self.SetAlpha(0.1) Self.DisableNoWait() Wait(0.5) BatFX02.TranslateToRef(ActorMelee, 500, 0.0) ActorMelee.Enable(True) DLC1VampireBatsVFX.Play(ActorMelee, 2) ActorMelee.EvaluatePackage() Wait(3.0) Self.MoveTo(XMDMagicSwapHeadingEnd) BatFX02.Disable(True) BatFX02.Delete() BatFX02 = None XMDBloodyPaybackMasterController01.Activate(Self) EndIf ENDFUNCTION I won't post my 2 main scripts because they reach 1375 lines. Plus the way I created this entire combat scene to ensure stability and a bug free scene, I ended up with 7 scripts just for the combat scene. Just a friendly advice: Save yourself some migraines, and leave this alone. Trust me on this, during the 6 years of my mod's development, I've tried every single possible approach... This here isn't the case of a simple function or line like the 'IsCommandedActor()', or a simple adjustment that you'll change something somewhere and it will work. Edited November 12, 2022 by maxarturo Link to comment Share on other sites More sharing options...
saintgrimm92 Posted November 12, 2022 Author Share Posted November 12, 2022 (edited) Except from the vanilla game Harkon boss fight and my own mod, I personally have never seen this done before in any mod I've played the last 12 years.And this is because the DLC1 vampire lord assets are completely broken, they are from the worst created assets by Bethesda, so in order to do the:- Transform to Vampire Lord > Vampire Lord Melee combat style > To Vampire Lord magic combat style (floating), and vise versa. 1) Except that you need 3 actors2) You have to do this inside a controlled combat scene, because you can't change combat style on the vamipre lord on the fly using the same actor, it just won't work!!This means that this requires a specific setup that you will end up with more than 1 script containing, as with my circumstance, thousands of lines!!! To get an idea of how this 'change combat style' is done on the vampire lord, take a look at how Bethesda coded the whole thing.And you need to take in account that 'that' specific vanilla combat scene despite the coding, is completely bugged!!!! Even here they F***** Up everything!! Script name = DLC1dunHarkonBatTeleportThis is the part where the change combat style is done, by actually changing actors and transfering their 'actor values'. Function SwapHarkons() ;;Debug.Trace("Swapping Harkons!") if (!initialized) initialized = True Initialize() EndIf HarkonBattleMagicFormActor.StopTranslation() HarkonBattleMeleeFormActor.StopTranslation() VQ08HarkonSwapMarker.MoveTo(SelfActor) if (SelfActor == HarkonBattleMagicFormActor) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonWarpMarker1) HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMagicFormActor, HarkonBattleMeleeFormActor) Self.ForceRefTo(HarkonBattleMeleeFormActor) OldActor = HarkonBattleMagicFormActor SelfActor = HarkonBattleMeleeFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMeleeFormActor ;;Debug.Trace("Melee Form, Go!") Else HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonWarpMarker2) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMeleeFormActor, HarkonBattleMagicFormActor) Self.ForceRefTo(HarkonBattleMagicFormActor) OldActor = HarkonBattleMeleeFormActor SelfActor = HarkonBattleMagicFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMagicFormActor ;;Debug.Trace("Magic Form, Go!") EndIf OldActor.StopCombat() SelfActor.SetSubGraphFloatVariable("fdampRate", 0.02) ;Fade in the new Harkon. SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0) ;;Debug.Trace("Swap complete. Self=" + Self.GetActorRef()) SelfActor.EvaluatePackage() EndFunction Function TransferAVs(Actor source, Actor destination) ;Debug.Trace("IN:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) if (destination.GetAV("Health") > source.GetAV("Health")) destination.DamageAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) Else destination.RestoreAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) EndIf ;Debug.Trace("Outgoing Health: " + source.GetAV("Health") + " == " + destination.GetAV("Health")) if (destination.GetAV("Magicka") > source.GetAV("Magicka")) destination.DamageAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) Else destination.RestoreAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) EndIf if (destination.GetAV("Stamina") > source.GetAV("Stamina")) destination.DamageAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) Else destination.RestoreAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) EndIf ;Debug.Trace("OUT:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) destination.SetAV("Variable06", 1) ;*Always* 1 at this point, since we're in the middle of a teleport. destination.SetAV("Variable07", source.GetAV("Variable07")) destination.SetAV("Variable10", source.GetAV("Variable10")) EndFunction This is the entire vanilla script handeling the actor: ScriptName DLC1dunHarkonBatTeleport extends ReferenceAlias {Script for Harkon's bat-based teleporting.} ;References for swapping between Harkon's Melee and Magic forms (which are actually different actors!). ReferenceAlias property HarkonBattleMainForm Auto ReferenceAlias property HarkonBattleMagicForm Auto ReferenceAlias property HarkonBattleMeleeForm Auto Actor property SelfActor Auto Hidden Actor property OldActor Auto Hidden Actor property HarkonBattleRealHarkonActor Auto Hidden Actor property HarkonBattleMeleeFormActor Auto Hidden Actor property HarkonBattleMagicFormActor Auto Hidden bool initialized ObjectReference property VQ08HarkonSwapMarker Auto ObjectReference property VQ08HarkonWarpMarker1 Auto ObjectReference property VQ08HarkonWarpMarker2 Auto ObjectReference property teleportObject auto hidden ;The location we're teleporting to. Spell property DLC1VQ08Bats auto ;Bat spell that handles the actual teleport. Spell property DLC1VQ08SwapBats auto ;Bat spell that handles the actual teleport. bool property swappingHarkons auto hidden ;Are we swapping Harkons on this teleport? ObjectReference property newHarkon auto Hidden ;If so, who are we swapping to? Function Initialize() SelfActor = Self.GetActorRef() HarkonBattleMeleeFormActor = HarkonBattleMeleeForm.GetActorRef() HarkonBattleMagicFormActor = HarkonBattleMagicForm.GetActorRef() EndFunction Function BatTeleportTo(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf teleportObject = obj swappingHarkons = False ;;Debug.Trace("Begin Harkon Teleport to " + teleportObject) ;;SelfActor.GetActorBase().SetInvulnerable(True) SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() SelfActor.DispelAllSpells() ;;Debug.Trace("Attempting teleport.") SelfActor.DoCombatSpellApply(DLC1VQ08Bats, Self.GetActorRef()) EndFunction Function BatTeleportAndSwapTo(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf teleportObject = obj swappingHarkons = True if (SelfActor == HarkonBattleMagicFormActor) newHarkon = HarkonBattleMeleeFormActor Else newHarkon = HarkonBattleMagicFormActor EndIf ;;Debug.Trace("Begin Harkon Swap Teleport to " + teleportObject) ;;SelfActor.GetActorBase().SetInvulnerable(True) HarkonBattleMagicFormActor.SetAV("Variable06", 1) HarkonBattleMagicFormActor.EvaluatePackage() HarkonBattleMagicFormActor.DispelAllSpells() ;HarkonBattleMagicFormActor.SetSubGraphFloatVariable("fdampRate", 0.20) ;Animation fadeout. ;HarkonBattleMagicFormActor.SetSubGraphFloatVariable("ftoggleBlend", 1.3) HarkonBattleMeleeFormActor.SetAV("Variable06", 1) HarkonBattleMeleeFormActor.EvaluatePackage() HarkonBattleMeleeFormActor.DispelAllSpells() ;HarkonBattleMeleeFormActor.SetSubGraphFloatVariable("fdampRate", 0.20) ;Animation fadeout. ;HarkonBattleMeleeFormActor.SetSubGraphFloatVariable("ftoggleBlend", 1.3) SelfActor.DoCombatSpellApply(DLC1VQ08SwapBats, newHarkon) EndFunction Function BatTeleportToEndMagic(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf if (SelfActor == HarkonBattleMagicFormActor) BatTeleportTo(obj) Else BatTeleportAndSwapTo(obj) EndIf EndFunction Function BatTeleportToEndMelee(ObjectReference obj) if (!initialized) initialized = True Initialize() EndIf if (SelfActor == HarkonBattleMeleeFormActor) BatTeleportTo(obj) Else BatTeleportAndSwapTo(obj) EndIf EndFunction Function SwapHarkons() ;;Debug.Trace("Swapping Harkons!") if (!initialized) initialized = True Initialize() EndIf HarkonBattleMagicFormActor.StopTranslation() HarkonBattleMeleeFormActor.StopTranslation() VQ08HarkonSwapMarker.MoveTo(SelfActor) if (SelfActor == HarkonBattleMagicFormActor) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonWarpMarker1) HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMagicFormActor, HarkonBattleMeleeFormActor) Self.ForceRefTo(HarkonBattleMeleeFormActor) OldActor = HarkonBattleMagicFormActor SelfActor = HarkonBattleMeleeFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMeleeFormActor ;;Debug.Trace("Melee Form, Go!") Else HarkonBattleMeleeFormActor.MoveTo(VQ08HarkonWarpMarker2) HarkonBattleMagicFormActor.MoveTo(VQ08HarkonSwapMarker) TransferAVs(HarkonBattleMeleeFormActor, HarkonBattleMagicFormActor) Self.ForceRefTo(HarkonBattleMagicFormActor) OldActor = HarkonBattleMeleeFormActor SelfActor = HarkonBattleMagicFormActor ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).SelfActor = HarkonBattleMagicFormActor ;;Debug.Trace("Magic Form, Go!") EndIf OldActor.StopCombat() SelfActor.SetSubGraphFloatVariable("fdampRate", 0.02) ;Fade in the new Harkon. SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0) ;;Debug.Trace("Swap complete. Self=" + Self.GetActorRef()) SelfActor.EvaluatePackage() EndFunction Function TransferAVs(Actor source, Actor destination) ;Debug.Trace("IN:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) if (destination.GetAV("Health") > source.GetAV("Health")) destination.DamageAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) Else destination.RestoreAV("Health", (-1 * (source.GetAV("Health") - destination.GetAV("Health")))) EndIf ;Debug.Trace("Outgoing Health: " + source.GetAV("Health") + " == " + destination.GetAV("Health")) if (destination.GetAV("Magicka") > source.GetAV("Magicka")) destination.DamageAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) Else destination.RestoreAV("Magicka", (-1 * (source.GetAV("Magicka") - destination.GetAV("Magicka")))) EndIf if (destination.GetAV("Stamina") > source.GetAV("Stamina")) destination.DamageAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) Else destination.RestoreAV("Stamina", (-1 * (source.GetAV("Stamina") - destination.GetAV("Stamina")))) EndIf ;Debug.Trace("OUT:") ;Debug.Trace("Source: " + source + ": " + source.GetAV("Health") + ", " + source.GetAV("Magicka") + ", " + source.GetAV("Stamina") + ", " + source.GetAV("Variable06")) ;Debug.Trace("Destination: " + destination + ": " + destination.GetAV("Health") + ", " + destination.GetAV("Magicka") + ", " + destination.GetAV("Stamina") + ", " + destination.GetAV("Variable06")) destination.SetAV("Variable06", 1) ;*Always* 1 at this point, since we're in the middle of a teleport. destination.SetAV("Variable07", source.GetAV("Variable07")) destination.SetAV("Variable10", source.GetAV("Variable10")) EndFunction Function BatsAllDone() if (swappingHarkons) SwapHarkons() EndIf if (SelfActor == HarkonBattleMagicFormActor) (SelfActor as DLC1HarkonCombatMagicLevelingScript).ReequipDrainSpell() EndIf SelfActor.SetAV("Variable06", 1) ((Self as ReferenceAlias) as DLC1dunHarkonBossBattle).HarkonReforms() EndFunction And this is the vanilla combat script inside the quest responsible for everything: ScriptName DLC1dunHarkonBossBattle extends ReferenceAlias {The main boss battle script for Harkon at the end of VQ08.} ;Harkon Actor Variables: ; - Variable06 is used as a package condition to control his combat state. ; 1 = Stay in Place (Wait for Teleport, Dying, Toggle, etc.) ; 2 = Shrine Event spellcasting ; 3-5 = Mistform Patrols ;Float that controls the state of the battle. Used to be an int, but I needed more granularity. ; 0-1 = Normal Combat ; 0 = Default Magic Combat. We stay in this for 60s or until a Shrine Event triggers. Teleports are to random positions. ; 0.25 = Default Melee Combat. We stay in this for 30s or until a Shrine Event triggers. Teleports are to nearby positions based on distance checks. ; 0.5 = Quick-Flip Combat. We may swap between magic and melee on each teleport. Teleports are determined by the form chosen. ; 1-3 = Starting Shrine Event 1-3 ; 4 = In Shrine Event ; 5 = In Mistform float property HarkonBossBattleState = 0.0 Auto Hidden ;VQ08 Quest this is an alias on. Quest property DLC1VQ08 Auto ReferenceAlias property HarkonBattleRealHarkon Auto ;Alias for the questline's Harkon. ReferenceAlias property HarkonBattleMeleeForm Auto ;Melee Form Harkon for this battle. ReferenceAlias property HarkonBattleMagicForm Auto ;Magic Form Harkon for this battle. Actor property SelfActor Auto Hidden Actor property HarkonBattleRealHarkonActor Auto Hidden Actor property HarkonBattleMeleeFormActor Auto Hidden Actor property HarkonBattleMagicFormActor Auto Hidden ;Whether the script has run its initialization (occurs on combat start). bool scriptVariablesInitialized bool initialized ;Unique spells manipulated by this script. Spell property DLC1dunHarkonConjureGargoyleLeftHand Auto Spell property DLC1HarkonDrain02Alt Auto Spell property DLC1HarkonMistform Auto Spell property DLC1dunHarkonInvulnerabilityShield Auto Spell property DLC1AbHarkonFloatBodyFX Auto bool SummonedInitialGargoyle ;Once Harkon summons a gargoyle or a shrine event triggers, we remove the spell. ;MAGIC/MELEE FORMS ReferenceAlias property HarkonBattleHoldPositionMarker Auto ;Where Magic Form Harkon is trying to get to. Effectively keeps him from running away all the time. ObjectReference property HarkonBattleHoldPositionMarkerObj Auto Hidden float TimeOfLastFormSwap = 0.0 float MaxTimeInMagicForm = 60.0 float MaxTimeInMeleeForm = 30.0 ;SHRINE EVENT ObjectReference property VQ08ShrineEventMarker Auto ;Location Harkon teleports to (atop shrine collision) ObjectReference property VQ08ShrineEventCollision Auto ;Collision to enable when Harkon is on the shrine. ObjectReference property VQ08EnemyTrigger1 Auto ;Traplinkers that hold the enemies to be activated. ObjectReference property VQ08EnemyTrigger2 Auto ObjectReference property VQ08EnemyTrigger3 Auto ObjectReference NextEnemyTrigger float ShrineEventTimestamp ;When the last Shrine Event triggered. float MinTimeBetweenShrineEvents = 8.0 ;Restricts when Harkon will next use a Shrine Event. float ShrineThreshold01 = 0.7 ;Health Thresholds that trigger the shrine events. float ShrineThreshold02 = 0.4 float ShrineThreshold03 = 0.2 bool ShrineEventActive = False ;Is a Shrine Event active? bool property ShieldDestroyed = False Auto Hidden ;Property checked by the shield magic effect to determine whether the shield blows up or gets dismissed quietly. int property LastShrineEvent = 0 Auto Hidden ;What Shrine Event did we do most recently? float ShrineEventFailsafeTimer = 0.0 ;Timer that kicks Harkon out of his Shrine event. Weapon property DLC1AurielsBow Auto ;Bow and Arrows to look for. Explosion property DLC1AurielsBowExp01 Auto Explosion property ExplosionIllusionDark01 Auto Ammo property DLC1ElvenArrowBlessed Auto Ammo property DLC1ElvenArrowBlood Auto bool property PlayerHasAurielsBow Auto Hidden Scene property DLC1VQ08HarkonBattleScene Auto ;Scene telling the player to use the bow. float property ShrineEventSceneTimer = 0.0 Auto hidden ;When did we last play this scene? ;MISTFORM ObjectReference property VQ08MistformEventMarker Auto ;Marker to translate to at start, to make sure Harkon doesn't get stuck on the shrine. ReferenceAlias property HarkonBattleNoNameAlias Auto ;Alias with an override empty name, to avoid awkward activation text. float MistformTimer ;Timer for when we started mistform. float MaxTimeInMistform = 20.0 ;Max time we'll stay in mistform. ;HARKON DEATH SCENE Spell property DLC1FXCastVampireBleedSpell Auto Armor property DLC1VampireSkeletonFXArmor Auto ObjectReference property DeadHarkonWarpMarker Auto bool inDeathThroesTeleport = False ;Are we in the final 'death throes' teleport? bool doneDeathThroesTeleport = False ;Have we finished the final 'death throes' teleport? bool doneFinalDeath = False ;Have we begun executing Harkon's OnDying block? int DeathThroesTeleportsLeft = 2 Activator property DLC1dunHarkonAshPile Auto ObjectReference property DLC1dunHarkonDeathFXAct Auto Sound property AmbRumbleShake Auto EffectShader property DLC1SunFireImpactFXShader Auto Spell property DLC1dunHarkonDeathSpell Auto ObjectReference property VQ08ExplosionSourceMarker Auto Explosion property DLC1VampiresBaneExplosion Auto ObjectReference property VQ08HarkonGroundMarker Auto Explosion property HarkonDeathExplosion Auto ;TELEPORTING ObjectReference[] property HarkonTeleportPoints Auto ;Array of possible teleport locations. int MultiTeleportCount = 0 ;Number of times to teleport in sequence. bool TeleportInProgress ;Is a teleport in progress? If so, prevent anything else from teleporting him. float MagicFormHoldPositionTimestamp = -1.0 float HealthPercentAtLastTeleport = 1.0 float MagicFormHealthPercentLossAllowed = 0.20 float MeleeFormHealthPercentLossAllowed = 0.30 float TimeOfLastTeleport = 0.0 float MaxTimeBetweenTeleports = 30.0 bool shouldTeleportCornered = False Static property HarkonTeleportMarker Auto ;The unique base object for Harkon's teleport markers. ;Harkon minions. ObjectReference[] property HarkonMinions Auto ;Hit Timestamps float TimerPreviousHit01 = 0.0 ;Timestamps that record when the player hits Harkon with an attacked. float TimerPreviousHit02 = 0.0 ;Used to approximately detect when he's been 'cornered' so he can escape. float TimerPreviousHit03 = 0.0 float TimerPreviousHit04 = 0.0 float TimerPreviousHit05 = 0.0 float TimerPreviousHit06 = 0.0 float TimerOfRecord = 0.0 ;Timer used to determine when Harkon was cornered. ;Harkon's Cape Armor property DLC1VampireLordCape Auto ;Music for the boss battle. Needed to prevent the audio from cutting in and out of combat constantly. MusicType property MUSCombatBoss Auto ;Vampire and Werewolf beast forms. Race property VampireBeastRace Auto Race property WerewolfBeastRace Auto ;-------------------------------------------------------------- ;Main Battle Loops: OnHit and OnUpdate ;--------------------------------------- Function InitializeHarkonBattle() if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf EndFunction Event OnCombatStateChanged(Actor akTarget, int aeCombatState) if (!initialized) initialized = True if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime TimeOfLastTeleport = currentTime PlayerHasAurielsBow = (Game.GetPlayer().GetItemCount(DLC1AurielsBow) > 0) ;Debug.Trace("Start the music!") MUSCombatBoss.Add() GoToState("Ready") OnUpdate() EndIf EndEvent Function InitializeScriptVariables() float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime TimeOfLastTeleport = currentTime SelfActor = Self.GetActorRef() HarkonBattleRealHarkonActor = HarkonBattleRealHarkon.GetActorRef() HarkonBattleMeleeFormActor = HarkonBattleMeleeForm.GetActorRef() HarkonBattleMagicFormActor = HarkonBattleMagicForm.GetActorRef() HarkonBattleMeleeFormActor.GetActorBase().SetEssential(False) HarkonBattleMagicFormActor.GetActorBase().SetEssential(False) HarkonBattleMeleeFormActor.StartDeferredKill() HarkonBattleMagicFormActor.StartDeferredKill() HarkonBattleHoldPositionMarkerObj = HarkonBattleHoldPositionMarker.GetReference() scriptVariablesInitialized = True EndFunction ;Every second, we check to see if we need to update Harkon's state. Function OnUpdate() If (!inDeathThroesTeleport) ProcessOnUpdateOROnHitEvent(None, None) RegisterForSingleUpdate(1) EndIf EndFunction Auto State Ready ;On hit, check to see which state we're in and respond accordingly. Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ProcessOnUpdateOROnHitEvent(aggressor, weap) EndEvent ;We have to handle both types of events in this single function to prevent interleaving errors from asynchronus events. Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) GoToState("Busy") ;Debug.Trace("HARKON: Harkon Processing----------") ;Debug.Trace("HARKON: PROCESSING: " + SelfActor + ", Health: " + SelfActor.GetAV("Health")) int activeMinionsCount = CountActiveMinions() float healthPercentage = SelfActor.GetAVPercentage("Health") float currentTime = Utility.GetCurrentRealTime() ;Are we dead or dying? If so, don't do anything. If (!initialized) ;Debug.Trace("HARKON: Not Initialized Yet") ;Do nothing! ElseIf (HarkonBossBattleState == 4 && inDeathThroesTeleport) ;If we're in the process of dying, but have wound up on the Shrine instead, bail out. EndShrineEvent(None, None) ElseIf (inDeathThroesTeleport) ;Debug.Trace("HARKON: In Death Throes") ;Do nothing! ElseIf (SelfActor.GetAV("Health") <= 0) ;Debug.Trace("HARKON: Triggering Death Throes") ;Start the Death Throes process. This is the only way we can catch the actor's 'death' because Deferred Kill is on. BeginDeathThroes() ElseIf (HarkonBossBattleState < 1) If (!SelfActor.IsInCombat()) SelfActor.StartCombat(Game.GetPlayer()) EndIf ;If we've passed one of the shrine health thresholds, that's our top priority. If (healthPercentage < ShrineThreshold01 && !ShrineEventActive && LastShrineEvent == 0) ;Debug.Trace("HARKON: Process SetupShrineEvent1") SetupShrineEvent(1) ElseIf (healthPercentage < ShrineThreshold02 && !ShrineEventActive && LastShrineEvent == 1 && (currentTime - ShrineEventTimestamp) > MinTimeBetweenShrineEvents) ;Debug.Trace("HARKON: Process SetupShrineEvent2") SetupShrineEvent(2) ElseIf (healthPercentage < ShrineThreshold03 && !ShrineEventActive && LastShrineEvent == 2 && (currentTime - ShrineEventTimestamp) > MinTimeBetweenShrineEvents) ;Debug.Trace("HARKON: Process SetupShrineEvent3") SetupShrineEvent(3) ElseIf (HarkonBossBattleState == 0 && currentTime - TimeOfLastFormSwap > MaxTimeInMagicForm) ;Debug.Trace("HARKON: Magic Form timer exceeded. Swapping forms.") TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0.25 TeleportHarkon() ElseIf (HarkonBossBattleState == 0.25 && currentTime - TimeOfLastFormSwap > MaxTimeInMeleeForm) ;Debug.Trace("HARKON: Melee Form timer exceeded. Swapping forms.") TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0 TeleportHarkon() ElseIf (SelfActor == HarkonBattleMagicFormActor && SelfActor.GetDistance(HarkonBattleHoldPositionMarker.GetReference()) < 400 && MagicFormHoldPositionTimestamp == -1) ;Debug.Trace("HARKON: Reached Hold Position marker. Setting Timestamp.") MagicFormHoldPositionTimestamp = currentTime ElseIf (SelfActor == HarkonBattleMagicFormActor && MagicFormHoldPositionTimestamp > 0 && currentTime - MagicFormHoldPositionTimestamp > 5) ;Debug.Trace("HARKON: Selecting new Hold Position Marker.") PickNewHoldPositionTargetWithEVP() ElseIf (SelfActor == HarkonBattleMagicFormActor && healthPercentage < (HealthPercentAtLastTeleport - MagicFormHealthPercentLossAllowed)) ;Debug.Trace("HARKON: Now Teleporting: Health Threshold (Magic) met.") TeleportHarkon() ElseIf (SelfActor == HarkonBattleMeleeFormActor && healthPercentage < (HealthPercentAtLastTeleport - MeleeFormHealthPercentLossAllowed)) ;Debug.Trace("HARKON: Now Teleporting: Health Threshold (Melee) met.") TeleportHarkon() ElseIf (shouldTeleportCornered && currentTime - TimerOfRecord < 4) ;Debug.Trace("HARKON: Waiting until full 4s are up.") ElseIf (shouldTeleportCornered && currentTime - TimerOfRecord >= 4) ;Debug.Trace("HARKON: Now Teleporting: Player cornered Harkon.") TeleportHarkon() ElseIf (currentTime - TimeOfLastTeleport > MaxTimeBetweenTeleports) ;Debug.Trace("HARKON: Now Teleporting: Max Time Between Teleports exceeded.") TeleportHarkon() ;Is Harkon in normal combat? If so, track hits so we can catch when he becomes 'stuck'. ElseIf (aggressor == Game.GetPlayer()) ;Debug.Trace("HARKON: Registered normal hit.") TimerPreviousHit06 = TimerPreviousHit05 TimerPreviousHit05 = TimerPreviousHit04 TimerPreviousHit04 = TimerPreviousHit03 TimerPreviousHit03 = TimerPreviousHit02 TimerPreviousHit02 = TimerPreviousHit01 TimerPreviousHit01 = currentTime if (SelfActor == HarkonBattleMagicFormActor && TimerPreviousHit01 - TimerPreviousHit04 < 6) shouldTeleportCornered = True TimerOfRecord = TimerPreviousHit04 ElseIf (SelfActor == HarkonBattleMeleeFormActor && TimerPreviousHit01 - TimerPreviousHit06 < 8) shouldTeleportCornered = True TimerOfRecord = TimerPreviousHit06 EndIf EndIf ElseIf (HarkonBossBattleState == 4) If (inDeathThroesTeleport) EndShrineEvent(None, None) ElseIf (activeMinionsCount == 0 && ShrineEventFailsafeTimer == 0) ;Debug.Trace("HARKON: Shrine Update: Setting Failsafe Timer") ShrineEventFailsafeTimer = currentTime ElseIf (activeMinionsCount > 0 && ShrineEventFailsafeTimer > 0) ;Debug.Trace("HARKON: Shrine Update: Jumped the gun. Resetting Shrine Event Failsafe Timer") ShrineEventFailsafeTimer = 0 ElseIf (activeMinionsCount == 0 && currentTime - ShrineEventFailsafeTimer >= 12) ;Debug.Trace("HARKON: Shrine Update: Failsafe Triggered") EndShrineEvent(None, None) ElseIf (ShrineEventSceneTimer == 0) ;Debug.Trace("HARKON: First time only, delay the scene.") ShrineEventSceneTimer = currentTime ElseIf (currentTime - ShrineEventSceneTimer > 15 && !DLC1VQ08HarkonBattleScene.IsPlaying() && PlayerHasAurielsBow) Race playerRace = Game.GetPlayer().GetRace() if ((VampireBeastRace == None || VampireBeastRace != playerRace) && (WerewolfBeastRace == None || WerewolfBeastRace != playerRace)) if (SelfActor.GetAV("Variable09") == 0) SelfActor.SetAV("Variable09", 1) EndIf ShrineEventSceneTimer = currentTime DLC1VQ08HarkonBattleScene.Start() EndIf EndIf ElseIf (HarkonBossBattleState == 5) ;If we're in Mistform, bail out if all of our minions are dead, or if time elapses. ;Debug.Trace("HARKON: Mistform Time: " + currentTime + ", " + MistformTimer) If (activeMinionsCount == 0) ;Debug.Trace("HARKON: Mistform Update: All minions dead.") EndMistform() ElseIf (currentTime > MistformTimer) ;Debug.Trace("HARKON: Mistform Update: Ending mistform by timer. " + Utility.GetCurrentRealTime() + " > " + MistformTimer) EndMistform() EndIf EndIf ;Debug.Trace("HARKON: ----------------------------") GoToState("Ready") EndFunction EndState State Busy Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ;Do Nothing EndEvent Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) ;Do Nothing EndFunction EndState Event OnHit(ObjectReference aggressor, Form weap, Projectile proj, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) ;Do nothing. EndEvent Function ProcessOnUpdateOROnHitEvent(ObjectReference aggressor, Form weap) ;Do Nothing EndFunction Function ProcessShadowShieldHit(ObjectReference aggressor, Form weap) ;This should all be handled by DLC1dunHarkonShadowShield now. ; ;If (HarkonBossBattleState == 4 && weap == DLC1AurielsBow) ; EndShrineEvent(aggressor as Actor, weap as Weapon) ;ElseIf (HarkonBossBattleState == 4 && weap != None && !DLC1VQ08HarkonBattleScene.IsPlaying() && Utility.GetCurrentRealTime() - ShrineEventSceneTimer > 5) ; ShrineEventSceneTimer = Utility.GetCurrentRealTime() ; DLC1VQ08HarkonBattleScene.Start() ;EndIf EndFunction ;Count how many 'active' enemies are left other than Harkon himself. int Function CountActiveMinions() int count = 0 int i = 0 Actor currentMinion While (i < HarkonMinions.Length) currentMinion = HarkonMinions[i] as Actor if (currentMinion.GetAV("Aggression") > 0 && !currentMinion.IsCommandedActor() && !currentMinion.IsDead()) count = count + 1 EndIf i = i + 1 EndWhile ;Debug.Trace("HARKON: Active Minions = " + count) return count EndFunction Function RecordSummonedGargoyle() if (!SummonedInitialGargoyle) ;Debug.Trace("HARKON: Gargoyle summoned. Removing the spell.") SummonedInitialGargoyle = True (SelfActor as DLC1HarkonCombatMagicLevelingScript).ReequipDrainSpell() EndIf SelfActor.RemoveSpell(DLC1dunHarkonConjureGargoyleLeftHand) EndFunction Function ResetHitClock() TimerPreviousHit01 = 0 TimerPreviousHit02 = 0 TimerPreviousHit03 = 0 TimerPreviousHit04 = 0 TimerPreviousHit05 = 0 TimerPreviousHit06 = 0 TimerOfRecord = 0 EndFunction ;-------------------------------------------------------------- ;Melee and Magic Forms ;--------------------- ;Harkon's Magic Form moves between Hold Position targets to allow him to move while still forcing him to stop periodically. Function PickNewHoldPositionTargetWithEVP() if (HarkonBossBattleState < 1 && !inDeathThroesTeleport) MagicFormHoldPositionTimestamp = -1 HarkonBattleHoldPositionMarker.ForceRefTo(PickTeleportMarkerRandomly()) if (SelfActor.GetAV("Variable06") <= 1) SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() EndIf if (SelfActor.GetAV("Variable06") <= 1) SelfActor.SetAV("Variable06", 0) SelfActor.EvaluatePackage() EndIf ;Debug.Trace("HARKON: PickNewHoldPositionTarget w/ EVPs resets Variable06 to 0.") EndIf EndFunction Function PickNewHoldPositionTargetWithoutEVP() MagicFormHoldPositionTimestamp = -1 HarkonBattleHoldPositionMarker.ForceRefTo(PickTeleportMarkerRandomly()) ;Debug.Trace("HARKON: PickNewHoldPositionTarget w/o EVPs picked a new target") EndFunction ;-------------------------------------------------------------- ;Teleport via Bats ;----------------- ;TRIGGERS: As Needed. Function TeleportHarkon() if (!TeleportInProgress && HarkonBossBattleState != 4) TeleportInProgress = True SelfActor.DispelAllSpells() ;Debug.Trace("HARKON: Now Teleporting. State: " + HarkonBossBattleState) if (inDeathThroesTeleport) ;Support legacy saves that had a higher number... if (DeathThroesTeleportsLeft > 2) DeathThroesTeleportsLeft = 2 EndIf if (DeathThroesTeleportsLeft == 2) BatTeleportToEndSameForm(HarkonTeleportPoints[11]) ElseIf (DeathThroesTeleportsLeft == 1) BatTeleportToEndSameForm(HarkonTeleportPoints[1]) ElseIf (DeathThroesTeleportsLeft == 0) BatTeleportToEndSameForm(HarkonTeleportPoints[0]) EndIf ElseIf (HarkonBossBattleState == 0) ;Standard magic teleport. BatTeleportToEndMagic(PickTeleportMarkerRandomly()) ElseIf (HarkonBossBattleState == 0.25) ;Standard melee teleport. BatTeleportToEndMelee(PickTeleportMarkerNearby()) ElseIf (HarkonBossBattleState == 0.5) int roll = Utility.RandomInt(0, 1) if (roll == 0) ;Debug.Trace("HARKON: Magic!") BatTeleportToEndMagic(PickTeleportMarkerRandomly()) Else ;Debug.Trace("HARKON: Melee!") BatTeleportToEndMelee(PickTeleportMarkerNearby()) EndIf Else ;Debug.Trace("HARKON: TeleportHarkon() called in invalid state: " + HarkonBossBattleState) EndIf EndIf EndFunction ObjectReference Function PickTeleportMarkerRandomly() int point = Utility.RandomInt(0, HarkonTeleportPoints.Length - 1) ;Debug.Trace("HARKON: Pick Randomly: " + point + ": " + HarkonTeleportPoints[point]) return HarkonTeleportPoints[point] EndFunction ObjectReference Function PickTeleportMarkerNearby() ObjectReference closestMarker = Game.FindClosestReferenceOfTypeFromRef(HarkonTeleportMarker, Game.GetPlayer(), 3000) if (closestMarker == None) closestMarker = PickTeleportMarkerRandomly() EndIf ;Identify the closest teleport point, offset from it +/-2, and go there. int closestPoint = 0 int i = 0 While (i < HarkonTeleportPoints.Length) if (HarkonTeleportPoints[i] == closestMarker) closestPoint = i i = HarkonTeleportPoints.Length EndIf i = i + 1 EndWhile int offset = Utility.RandomInt(0, 1) if (offset == 0) offset = -1 EndIf int targetPoint = closestPoint + offset if (targetPoint >= HarkonTeleportPoints.Length) targetPoint = 0 ElseIf (targetPoint < 0) targetPoint = HarkonTeleportPoints.Length - 1 EndIf return HarkonTeleportPoints[targetPoint] EndFunction Function BatTeleportToEndSameForm(ObjectReference target) if (SelfActor == HarkonBattleMagicFormActor) BatTeleportToEndMagic(target) Else BatTeleportToEndMelee(target) EndIf EndFunction Function BatTeleportToEndSwapForm(ObjectReference target) if (SelfActor == HarkonBattleMagicFormActor) BatTeleportToEndMelee(target) Else BatTeleportToEndMagic(target) EndIf EndFunction Function BatTeleportToEndMagic(ObjectReference target) ;Debug.Trace("HARKON: BatTeleportToEndMagic called.") ;Debug.Trace("HARKON: Updating health thresholds.") HealthPercentAtLastTeleport = SelfActor.GetAVPercentage("Health") TimeOfLastTeleport = Utility.GetCurrentRealTime() PickNewHoldPositionTargetWithoutEVP() TimerOfRecord = 0 shouldTeleportCornered = False ((Self as ReferenceAlias) as DLC1dunHarkonBatTeleport).BatTeleportToEndMagic(target) EndFunction Function BatTeleportToEndMelee(ObjectReference target) ;Debug.Trace("HARKON: BatTeleportToEndMelee called.") ;Debug.Trace("HARKON: Updating health thresholds.") HealthPercentAtLastTeleport = SelfActor.GetAVPercentage("Health") TimeOfLastTeleport = Utility.GetCurrentRealTime() TimerOfRecord = 0 shouldTeleportCornered = False ((Self as ReferenceAlias) as DLC1dunHarkonBatTeleport).BatTeleportToEndMelee(target) EndFunction Function HarkonReforms() ;Harkon is reforming from bat form. Determine what, if anything, we want to do in response. ;Debug.Trace("HARKON: Harkon Reformed.") HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) ;Make sure we don't stay in the teleport package. if (HarkonBossBattleState != 4 && !inDeathThroesTeleport) SelfActor.SetAV("Variable06", 0) EndIf ;Allow teleports going forward. TeleportInProgress = False ResetHitClock() If (HarkonBossBattleState == 1 || HarkonBossBattleState == 2 || HarkonBossBattleState == 3) ;Debug.Trace("HARKON: Harkon Starts Shrine") StartShrineEvent() ElseIf (inDeathThroesTeleport) if (SelfActor == HarkonBattleMagicFormActor) ;Debug.Trace("HARKON: Moving Melee Harkon to platform.") HarkonBattleMeleeFormActor.MoveTo(DeadHarkonWarpMarker) Else ;Debug.Trace("HARKON: Moving Magic Harkon to platform.") HarkonBattleMagicFormActor.MoveTo(DeadHarkonWarpMarker) EndIf if (SelfActor.GetAV("Variable10") <= 0) SelfActor.SetAV("Variable10", 1) Else SelfActor.SetAV("Variable10", 0) EndIf if (DeathThroesTeleportsLeft > 0) DeathThroesTeleportsLeft = DeathThroesTeleportsLeft - 1 SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() TeleportHarkon() Else ;Debug.Trace("HARKON: Harkon Reformed. Kill him.") doneDeathThroesTeleport = True SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() EndDeathThroes() EndIf ElseIf (MultiTeleportCount > 0) MultiTeleportCount = MultiTeleportCount - 1 TeleportHarkon() Else SelfActor.StartCombat(Game.GetPlayer()) EndIf SelfActor.EvaluatePackage() EndFunction ;-------------------------------------------------------------- ;Shrine Events ;--------------- ;TRIGGERS: Health Thresholds. ;Trigger enemies and warp Harkon to the Shrine. Function SetupShrineEvent(int eventNum) If (TeleportInProgress) Return EndIf SelfActor.DispelAllSpells() TeleportInProgress = True ShrineEventActive = True if (eventNum == 1) NextEnemyTrigger = VQ08EnemyTrigger1 ;Strip the Summon Gargoyle spell, if it hasn't been used already. RecordSummonedGargoyle() ElseIf (eventNum == 2) NextEnemyTrigger = VQ08EnemyTrigger2 Else NextEnemyTrigger = VQ08EnemyTrigger3 EndIf HarkonBossBattleState = eventNum LastShrineEvent = eventNum VQ08ShrineEventCollision.Enable() BatTeleportToEndMagic(VQ08ShrineEventMarker) EndFunction ;Activate Harkon's shield spell. Function StartShrineEvent() ;Update Harkon's AI so he stays put and casts his invulnerability shield spell. if (SelfActor.GetAV("Health") < 0) if (inDeathThroesTeleport) HarkonBossBattleState = 0 TeleportHarkon() Else BeginDeathThroes() EndIf Return EndIf SelfActor.EquipSpell(DLC1dunHarkonInvulnerabilityShield, 1) SelfActor.SetAV("Variable06", 2) SelfActor.EvaluatePackage() NextEnemyTrigger.Activate(SelfActor) if (LastShrineEvent > 1) ShrineEventSceneTimer = Utility.GetCurrentRealTime() EndIf HarkonBossBattleState = 4 EndFunction ;Cancel shield spell and complete shrine event. Function EndShrineEvent(Actor aggressorIfAny, Weapon weaponIfAny) if (HarkonBossBattleState == 4) ;Clean up the Shrine event. ;Debug.Trace("HARKON: Shrine Event is ending. Clean up.") float currentTime = Utility.GetCurrentRealTime() TimeOfLastFormSwap = currentTime HarkonBossBattleState = 0 if (weaponIfAny == DLC1AurielsBow) ShieldDestroyed = True SelfActor.RampRumble(0.75, 2, 1600) Game.ShakeCamera(SelfActor, 0.75, 2) Else ShieldDestroyed = False EndIf SelfActor.SetAV("Variable06", 1) SelfActor.EvaluatePackage() SelfActor.InterruptCast() SelfActor.DispelSpell(DLC1dunHarkonInvulnerabilityShield) HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) if (weaponIfAny == DLC1AurielsBow) if (AggressorIfAny.IsEquipped(DLC1ElvenArrowBlessed)) ;Debug.Trace("HARKON: Sunhallowed Arrow: Big explosion! Big damage!") ;SelfActor.PlaceAtMe(DLC1AurielsBowExp01) DealShrineEventHealthDamage(50) ElseIf (!AggressorIfAny.IsEquipped(DLC1ElvenArrowBlood)) ;Debug.Trace("HARKON: Normal Arrow: Explosion! Damage! Stagger!") ;SelfActor.PlaceAtMe(DLC1AurielsBowExp01) DealShrineEventHealthDamage(25) Else ;Debug.Trace("HARKON: Blood Arrow: Small explosion. Just End It!") ;SelfActor.PlaceAtMe(ExplosionIllusionDark01) EndIf Else ;("Shrine event ended normally.") EndIf ShrineEventActive = False ShrineEventTimestamp = currentTime ShrineEventFailsafeTimer = 0 ;Debug.Trace("HARKON: Evaluating next step. " + LastShrineEvent + ", " + CountActiveMinions()) if (inDeathThroesTeleport) TeleportHarkon() ElseIf (LastShrineEvent == 2 && CountActiveMinions() > 0) StartMistform() ElseIf ((LastShrineEvent == 1 || LastShrineEvent == 3) && CountActiveMinions() > 1) StartMistform() Else SelectPostShrineCombatStage() TeleportHarkon() EndIf VQ08ShrineEventCollision.Disable() EndIf EndFunction Function SelectPostShrineCombatStage() if (LastShrineEvent < 2) HarkonBossBattleState = 0 ElseIf (LastShrineEvent == 2) HarkonBossBattleState = 0.25 Else HarkonBossBattleState = 0.5 EndIf EndFunction Function DealShrineEventHealthDamage(int damage) if (SelfActor.GetAV("Health") > damage) SelfActor.DamageAV("Health", damage) Else SelfActor.DamageAV("Health", (SelfActor.GetAV("Health") - 1)) EndIf EndFunction ;-------------------------------------------------------------- ;Mistform Events ;--------------- ;TRIGGERS: After a Shrine Event, if 2+ enemies are active. Function StartMistform() ;Begin running a mistform patrol package. int patrol = Utility.RandomInt(0, 1) SelfActor.SetAV("Variable06", (3 + patrol)) SelfActor.EvaluatePackage() ;Cast Mistform and hide Harkon's name so he can't be selected. DLC1HarkonMistform.Cast(SelfActor, SelfActor) SelfActor.TranslateToRef(VQ08MistformEventMarker, 6000.0, 0.0) HarkonBattleNoNameAlias.ForceRefTo(SelfActor) ;Set timestamp and battle state. MistformTimer = Utility.GetCurrentRealTime() + MaxTimeInMistform HarkonBossBattleState = 5 ;Debug.Trace("HARKON: Harkon entered mistform and started patrol " + (3+patrol) + " with timer " + MistformTimer) EndFunction Function EndMistform() TimeOfLastFormSwap = Utility.GetCurrentRealTime() SelectPostShrineCombatStage() SelfActor.DispelSpell(DLC1HarkonMistform) HarkonBattleNoNameAlias.Clear() HarkonBattleMagicFormActor.GetActorBase().SetInvulnerable(False) HarkonBattleMeleeFormActor.GetActorBase().SetInvulnerable(False) SelfActor.SetAV("Variable06", 0) SelfActor.StartCombat(Game.GetPlayer()) SelfActor.EvaluatePackage() ;Debug.Trace("HARKON: Harkon exited mistform.") EndFunction ;-------------------------------------------------------------- ;Death Events ;------------ Event OnDeath(Actor akKiller) if (!doneFinalDeath) Debug.Trace("ERROR: HARKON HAS DIED PREMATURELY.") doneDeathThroesTeleport = True EndDeathThroes() EndIf EndEvent Function BeginDeathThroes() if (!scriptVariablesInitialized) InitializeScriptVariables() EndIf if (!inDeathThroesTeleport) inDeathThroesTeleport = True SelfActor.BlockActivation() SelfActor.GetActorBase().SetInvulnerable(True) ;Kill any surviving minions. int i = 0 While (i < HarkonMinions.Length) if ((HarkonMinions[i] as DLC1dunHarkonReanimatedMinions) != None) (HarkonMinions[i] as DLC1dunHarkonReanimatedMinions).IsSpecialDeath = True EndIf if (!HarkonMinions[i].IsDisabled()) (HarkonMinions[i] as Actor).Kill() EndIf i = i + 1 EndWhile TeleportHarkon() EndIf EndFunction Event EndDeathThroes() If (doneDeathThroesTeleport && !doneFinalDeath) ;Debug.Trace("HARKON: End death teleport. Harkon killed by script.") ;Begin disintegration and vfx. doneFinalDeath = True MUSCombatBoss.Remove() SelfActor.SetSubGraphFloatVariable("fdampRate", 1.0) ;;speeds up fade rate (max 1 min .1 SelfActor.SetSubGraphFloatVariable("ftoggleBlend", 0.0);;blends between two anims default 0 (0 = there 1 = gone) DLC1FXCastVampireBleedSpell.Cast(SelfActor, SelfActor) VQ08HarkonGroundMarker.MoveTo(SelfActor, 0, 0, -1, False) DLC1dunHarkonDeathFXAct.MoveTo(VQ08HarkonGroundMarker) DLC1dunHarkonDeathFXAct.PlayAnimation("PlayAnim01") SelfActor.BlockActivation(True) SelfActor.SetGhost(True) SelfActor.ClearExtraArrows() ;This is almost certainly unnecessary, but just to be safe, we'll end the deferred kill state for both actors, then make sure the current one will die. HarkonBattleMagicFormActor.EndDeferredKill() HarkonBattleMeleeFormActor.EndDeferredKill() ;SelfActor.Kill() SelfActor.SetCriticalStage(SelfActor.CritStage_DisintegrateStart) Utility.Wait(1) AmbRumbleShake.Play(VQ08HarkonGroundMarker) Game.GetPlayer().RampRumble(1, 2, 1600) Game.ShakeCamera(SelfActor, 1, 2) SelfActor.PlaceAtMe(HarkonDeathExplosion) ;Pull in the 'Real' Harkon and make him the ash pile, so the player doesn't lose any items ;they may have reverse-pickpocketed onto him during the questline. ;Wait for Harkon to collapse. Utility.Wait(4) HarkonBattleRealHarkonActor.GetActorBase().SetEssential(False) HarkonBattleRealHarkonActor.Kill() HarkonBattleRealHarkonActor.SetCriticalStage(HarkonBattleRealHarkonActor.CritStage_DisintegrateStart) HarkonBattleRealHarkonActor.MoveTo(VQ08HarkonGroundMarker) HarkonBattleRealHarkonActor.AttachAshPile(DLC1dunHarkonAshPile) HarkonBattleRealHarkonActor.SetCriticalStage(HarkonBattleRealHarkonActor.CritStage_DisintegrateEnd) HarkonBattleRealHarkonActor.Enable() Utility.Wait(10) ;Finish dissolving Harkon. SelfActor.SetCriticalStage(SelfActor.CritStage_DisintegrateEnd) ;Complete and shut down quest. DLC1VQ08.SetStage(200) EndIf EndEvent And here is my approach to change combat style / change actors which is done in my mod, I'm only posting the function that swamp actors, and not the whole script:* The obtain 'actor's AV' and store it, is done by another function not living inside this one. Here is a 'fixed' function that serves as a 'Fail Safe' inside the script, just in case something went wrong with the player's playthrough this will fire instead of the more demanding function. FUNCTION SwapActor() If ( IsMelee == True ) SearchGoal() If ( FoundTele == True ) ObjectReference BatFX01 = Self.PlaceAtMe(XMDBatsFXActivator01, 1) Wait(0.5) Self.DisableNoWait() ActorMagic.Enable() ActorMagic.DamageActorValue("Health", ActorMagic.GetActorValue("Health") * 0.50) ActorMagic.TranslateToRef(TeleportGoal, 16000.0, 0.0) DLC1VampireBatsVFX.Play(ActorMagic, 2) While ( ActorReady == False ) If ( ActorMagic.IsDisabled() == False ) && ( ActorMagic.getDistance(TeleportGoal) <= 512 ) ActorReady = True Else ActorMagic.Enable() ActorMagic.TranslateToRef(TeleportGoal, 16000.0, 0.0) DLC1VampireBatsVFX.Play(ActorMagic, 2) EndIf EndWhile Wait(0.5) BatFX01.TranslateToRef(teleportGoal, 500, 0.0) ActorMagic.StartCombat(opponent as actor) Self.RemoveFromFaction(XMD_XMVampireFaction) Self.AddToFaction(XMDvampLegionFriendlyFaction) Self.SetActorValue("Aggression", 0) Self.MoveTo(XMDMeleeSwapHeading) BatFX01.Delete() BatFX01 = None Else SearchGoal() EndIf Else ObjectReference BatFX02 = Self.PlaceAtMe(XMDBatsFXActivator01, 1) Wait(0.5) Self.SetAlpha(0.1) Self.DisableNoWait() Wait(0.5) BatFX02.TranslateToRef(ActorMelee, 500, 0.0) ActorMelee.Enable(True) DLC1VampireBatsVFX.Play(ActorMelee, 2) ActorMelee.EvaluatePackage() Wait(3.0) Self.MoveTo(XMDMagicSwapHeadingEnd) BatFX02.Disable(True) BatFX02.Delete() BatFX02 = None XMDBloodyPaybackMasterController01.Activate(Self) EndIf ENDFUNCTION I won't post my 2 main scripts because they reach 1375 lines. Plus the way I created this entire combat scene to ensure stability and a bug free scene, I ended up with 7 scripts just for the combat scene. Just a friendly advice:Save yourself some migraines, and leave this alone.Trust me on this, during the 6 years of my mod's development, I've tried every single possible approach... This here isn't the case of a simple function or line like the 'IsCommandedActor()', or a simple adjustment that you'll change something somewhere and it will work. That's just f***ing great V_V I've got lines of dialogue referring to the NPC being summoned. It's the "big reward" following the big finale.... Also got permission to use the unique vampire lord models by promising there'd be a unique power to summon the NPC..... The same NPC is intended to help the player fight the final boss and I just finished setting up the scene that ends with the lord and final boss being added to opposing factions - Guess there's no need to test that fight right now. I'm not the best at manual weights, 95% of the time I use blender's boneweight copy script. Actually 100% of the time I do, but about 5% of the time I have to manually touch up stuff. But I suppose I can try my best to get the unique vamp lord model to work with a dragonpriest skeleton or something.. Sounding like the best option at least. Will have static wings and the float might look a little weird, but if I can get the weights right, might look acceptable at least. EDIT: No one knows how Mihail did it? He had an NPC mod that added skeletal vampire lords and other unique vampire lord models as NPC enemies in the wild. I have the models in an old archive. But no scripts or ESP to see how it was done, and he's also removed the mod from the nexus, so I can't get the full thing to check. Maybe it was removed because it didn't work, idk... But I do know all of his mods descriptions say he doesn't have time to reply to PMs so I don't see a point in asking him directly. EDIT2: Mihail's Grevious twilight is also listed as a vampire lord NPC that does combat with the player. It's also been removed from the nexus :wallbash: Edited November 12, 2022 by saintgrimm92 Link to comment Share on other sites More sharing options...
maxarturo Posted November 12, 2022 Share Posted November 12, 2022 You can just use the Harkon Magic as it is, actually duplicate it and give it a unique ID, that actor will float and use magic attacks when the opponent gets to 3 meters distance of him, but he will not use melee attacks, and when he lands he can't counter attack when he gets hit from 3 meters+. Link to comment Share on other sites More sharing options...
saintgrimm92 Posted November 12, 2022 Author Share Posted November 12, 2022 (edited) You can just use the Harkon Magic as it is, actually duplicate it and give it a unique ID, that actor will float and use magic attacks when the opponent gets to 3 meters distance of him, but he will not use melee attacks, and when he lands he can't counter attack when he gets hit from 3 meters+. This NPC only uses magic, so having him in his magic form constantly is exactly what I'm going for. It'd be cool if he was just walking outside of combat, but it's not important at all that he does that. If always floating will make him fight with magic 100% of the time, I'm more than happy to do that. But my NPC already copies MOST of everything from the Harkon Magic NPC. He still starts out in melee form though, and just runs around doing nothing for seemingly a random amount of time. Then he starts floating and doing what he's supposed to do. Sometimes I have all the enemies killed before he even starts to float. Sometimes he goes right into float mode and starts fighting. I'm not sure how far 3 meters looks in-game, but a majority of the time, if he's NOT floating, he will just turn around and run away from the enemy. Then if/when he starts to float, he comes back and attacks. EDIT:The magic Harkon does have a script my NPC doesn't have. But looking at the script, all it does is add certain spells to the NPC depending on the player's level. My NPC uses custom spells that are manually added to the Actor's spell list, so idt he needs the script. Everything else is exactly like the magic harkon, except the skin, as he uses a custom vampire lord model/texture. He casts the custom spells fine, but only in floating mode. I just need to figure out a way to force him to float constantly I guess. He does have all the BodyFX and those types of spells the script add to harkon as well, the only ones he's missing are the attack spells because he casts my spells instead. Edited November 12, 2022 by saintgrimm92 Link to comment Share on other sites More sharing options...
Sphered Posted November 12, 2022 Share Posted November 12, 2022 You could make a hybrid Basically you can adapt a vampirelord to be a dragonpriest under the hood, by using a custom skeleton.nif which has nodes for both races. This would use Priest behaviors (hovering) while at the same time using VLord skin and abilities Not a quick easy thing to sculpt, and would have quirks, but if committed you could pull off the theme you are going for Could also in this approach, use SetRace() to go between the hybrid race and full vlord race on demand. Comes down to how much you want something like this to happen Link to comment Share on other sites More sharing options...
saintgrimm92 Posted November 12, 2022 Author Share Posted November 12, 2022 (edited) EDIT:NOW FULLY WORKING.How the NPC works:Summoned on feet/walking.Upon entering combat, begins to float/casting spells.After all nearby enemies are dead, lands on feet/begins walking again. Script for anyone who needs it: Scriptname AAVampKingCombatScript extends Actor Bool Property isSneaking = False Auto Hidden EVENT OnCombatStateChanged(Actor akTarget, int aeCombatState) If (aeCombatState == 1) If ( isSneaking == False ) StartSneaking() isSneaking = True EndIf ElseIf (aeCombatState == 0) StartSneaking() isSneaking = False EndIf EndEventEdited further to remove redundant Info. Edited November 13, 2022 by saintgrimm92 Link to comment Share on other sites More sharing options...
Recommended Posts