From 623d660f55f5b31a840980ed37caad1df93152ac Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Thu, 11 Aug 2022 21:27:16 +0200 Subject: [PATCH] Added the Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage --- scripts/dragonactorscript.pex | Bin 0 -> 8025 bytes source/scripts/dragonactorscript.psc | 304 +++++++++++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 scripts/dragonactorscript.pex create mode 100644 source/scripts/dragonactorscript.psc diff --git a/scripts/dragonactorscript.pex b/scripts/dragonactorscript.pex new file mode 100644 index 0000000000000000000000000000000000000000..ea7a699a497d0b77ff1c91c9fcb89f12ea83de2c GIT binary patch literal 8025 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 literal 0 HcmV?d00001 diff --git a/source/scripts/dragonactorscript.psc b/source/scripts/dragonactorscript.psc new file mode 100644 index 00000000..5234afc7 --- /dev/null +++ b/source/scripts/dragonactorscript.psc @@ -0,0 +1,304 @@ +scriptname dragonActorSCRIPT extends actor +{Base Actor Script for all Dragons} +; _____________ +;| | +;| SETUP | +;|_____________| +ImageSpaceModifier property dragonFOVfx auto +{FX played for various impacts in dragon combat} +float property deathFXrange = 1024.0 auto +{max range to play death FX on player} +quest property MQkillDragon auto +{used to invoke deathSequence() function in MQKillDragonScript.psc} +actor property player auto hidden +{For quick reference and clean-looking script} +float property FOVfalloff auto hidden +{choosing not to expose this and clutter the prop list in CS, since it won't be touched often} +sound property NPCDragonFlyby auto +{Sound played when dragon passes by a target} +explosion property knockBackExplosion auto +{explosion used to knock back enemies} +; NOTE - the dragon breed/skin settings are deprecated and have no effect. +armor property SnowDragonSkin auto +{deprecated - do not use} +armor property TundraDragonSkin auto +{deprecated - do not use} +armor property forestDragonSkin auto +{deprecated - do not use} +int property dragonBreed = 0 auto +{deprecated - do not use} + +WIFunctionsScript Property WI Auto ;added by jduvall +{Pointer to WIFunctionsScript on WI quest. Used to create script event to get nearby NPCs to react to the death of the dragon} + +ImpactDataSet Property FXDragonTakeoffImpactSet Auto +{Impact data set to use for the dragon takeoff} +ImpactDataSet Property FXDragonLandingImpactSet Auto +{Impact data set to use for the dragon landing} +ImpactDataSet Property FXDragonTailstompImpactSet Auto +{Impact data set to use for the tailstomp} + + +;*****ADDED FOR DLC2-------------- +bool MiraakIntroductionHappened +bool MiraakAppeared +Location Property DLC2ApocryphaLocation Auto +WorldSpace Property DLC2ApocryphaWorld auto +;*****---------------------------- + +; _____________ +;| | +;| EVENTS | +;|_____________| + EVENT onInit() + ; just initialize any variables, etc. + player = game.getPlayer() ; store player reference + FOVfalloff = 1600 ; range at which impact doesn't play FOV + + if deathFXrange == 0 + deathFXrange = 1000 + endif + + if !isDead() && isGhost() + ; redundancy check to prevent invincible, "ghosted" dragons from respawning. + setGhost(FALSE) + endif + + gotoState("alive") + endEVENT + + EVENT onReset() + ; if we're resetting a previously-killed dragon, make sure it's not a ghost. + setGhost(FALSE) + endEVENT + + EVENT onLoad() + ; change markings based on breed of this dragon + ; first, support random selection + ; if dragonBreed == 4 + ; dragonBreed = utility.randomInt(0,3) + ; endif + ; then assign the markings, either way. + ; if dragonBreed == 1 + ; equipItem(snowDragonSkin) + ; elseif dragonBreed == 2 + ; equipItem(tundraDragonSkin) + ; elseif dragonBreed == 3 + ; equipItem(forestDragonSkin) + ; endif + + ; Block rewritten by USKP which fixes bug of respawned dragons not burning up or giving the player a soul + if !isDead() + + if isGhost() + ; redundancy check to prevent invincible, "ghosted" dragons from respawning. + setGhost(FALSE) + endif + + ; Block rewritten by tarlazo to fix a possible rare glitch as consequence of animation events registered for a dead dragon + registerForAnimationEvent(self, "DragonLandEffect") + registerForAnimationEvent(self, "DragonForcefulLandEffect") + registerForAnimationEvent(self, "DragonTakeoffEffect") + registerForAnimationEvent(self, "DragonBiteEffect") + registerForAnimationEvent(self, "DragonTailAttackEffect") + registerForAnimationEvent(self, "DragonLeftWingAttackEffect") + registerForAnimationEvent(self, "DragonRightWingAttackEffect") + registerForAnimationEvent(self, "DragonPassByEffect") + registerForAnimationEvent(self, "flightCrashLandStart") ; Considered temp for showing injury FX test + registerForAnimationEVENT(self, "DragonKnockbackEvent") + + gotoState("alive") + endif + + ; registerForAnimationEvent(self, "DragonLandEffect") + ; registerForAnimationEvent(self, "DragonForcefulLandEffect") + ; registerForAnimationEvent(self, "DragonTakeoffEffect") + ; registerForAnimationEvent(self, "DragonBiteEffect") + ; registerForAnimationEvent(self, "DragonTailAttackEffect") + ; registerForAnimationEvent(self, "DragonLeftWingAttackEffect") + ; registerForAnimationEvent(self, "DragonRightWingAttackEffect") + ; registerForAnimationEvent(self, "DragonPassByEffect") + ; registerForAnimationEvent(self, "flightCrashLandStart") ; Considered temp for showing injury FX test + ; registerForAnimationEVENT(self, "DragonKnockbackEvent") + endEVENT + + +Event OnLocationChange(Location akOldLoc, Location akNewLoc) + ;USED TO GET DIALOGUE CONDITIONED ON DRAGON HAVING ATTACKED A TOWN -- only happens if he lands on the ground in a location - ie death, or to land to fight (not on a perch) + ;see also DragonPerchScript + +; debug.trace(self + "OnLocationChange() calling WI.RegisterDragonAttack(" +akNewLoc + ")") + ;USKP 2.0.3 - So. Now it seems like you won't even call this out of here with a None? Ok..... + if( akNewLoc != None ) + WI.RegisterDragonAttack(akNewLoc, self) + EndIf + + if !isDead() && isGhost() + ; redundancy check to prevent invincible, "ghosted" dragons from respawning. + setGhost(FALSE) + endif + +EndEvent + +STATE alive + Event OnCombatStateChanged(Actor akTarget, int aeCombatState) + + if !isDead() && isGhost() + ; redundancy check to prevent invincible, "ghosted" dragons from respawning. + setGhost(FALSE) + endif + + if akTarget == Game.GetPlayer() + WI.updateWIDragonTimer() ;used to prevent dragons from appearing too frequently + endif + endEvent + + EVENT onAnimationEvent(objectReference deliverator, string eventName) +; debug.trace("AnimEvent: " + eventName + " delivered by Dragon ("+self+") ") + if (eventName == "DragonLandEffect") +; ;debug.trace("Dragon AnimPayLoad: DragonLandEffect") + game.shakeCamera(self, 1) + game.shakeController(95, 95, 2) + KnockAreaEffect(1, getLength()) + animateFOV() ; call the FOV function + PlayImpactEffect(FXDragonTakeoffImpactSet, "NPC Pelvis", 0, 0, -1, 512) + elseIf (eventName == "DragonForcefulLandEffect") +; ;debug.trace("Dragon AnimPayLoad: DragonForcefulLandEffect") + PlayImpactEffect(FXDragonLandingImpactSet, "NPC Pelvis", 0, 0, -1, 512) + KnockAreaEffect(1, 2*getLength()) + ;notification ("Dragon Forceful Land Effect") + elseIf (eventName == "DragonTakeoffEffect") +; debug.trace("Dragon AnimPayLoad: DragonTakeoffEffect") + PlayImpactEffect(FXDragonTakeoffImpactSet, "NPC Tail8", 0, 0, -1, 2048) + elseIf (eventName == "DragonBiteEffect") + ;Removed spit effect on 6/1/10. Leaving logic in case we need to sub in other fx or sounds. +; ;debug.trace("Dragon ("+self+") AnimPayLoad: DragonBiteEffect") + ;DragonBiteFX.Play(Self, 2) + ;notification ("Dragon Bite Effect") + elseIf (eventName == "DragonTailAttackEffect") +; ;debug.trace(" Dragon AnimPayoload: DragonTailAttackEffect") + PlayImpactEffect(FXDragonTailstompImpactSet, "NPC Tail8", 0, 0, -1, 512) + ;notification("Dragon Tail Attack Effect") + elseIf (eventName == "DragonLeftWingAttackEffect") +; ;debug.trace("Dragon AnimPayLoad: DragonLeftWingAttackEffect") + PlayImpactEffect(FXDragonTailstompImpactSet, "NPC LHand", 0, 0, -1, 512) + ;notification ("Dragon Left Wing Attack Effect") + elseIf (eventName == "DragonRightWingAttackEffect") +; ;debug.trace("Dragon AnimPayLoad: DragonRightWingAttackEffect") + PlayImpactEffect(FXDragonTailstompImpactSet, "NPC RHand", 0, 0, -1, 512) + ;notification ("Dragon Right Wing Attack Effect") + elseIf (eventName == "DragonPassByEffect") + NPCDragonFlyby.Play(self) + game.shakeCamera(self, 0.85) + game.shakeController(0.65, 0.65, 0.5) + ;elseIf (eventName == "flightCrashLandStart") + ;Removed by mark since leaking magic is a bust. May replace with leaking blood at some point as a second test. + ; apply injury FX art when I crash land. + ;FXinjuryLeaks.play(self, -1) + elseIf (eventName == "DragonKnockbackEvent") + ; dragon needs to stagger everyone in radius a bit larger than my length +; ;debug.trace(self + " is knocking back actors within " + 1.5*getLength() + " of self @ " + self.x + ", " + self.y) + ;self.placeAtMe(knockBackExplosion) + KnockAreaEffect(1, 1.5*getLength()) + animateFOV(1.5*getLength()) + endif + endEVENT + + EVENT onDeath(actor killer) + ;debug.trace("Remove mouth/injury FX art, begin death FX sequence for " + self) + ;USKP 2.1.3 Bug #19214 - Dialogue in the quest has options that get skipped with this line below the gotoState call. + WI.startWIDragonKillQuest(self) ;used to create a scene if any NPCs are nearby when the dragon dies. See WIFunctionsScript attached to WI quest which creates a story manager script event, and WIDragonKilled quest which handles the scene. + gotoState("deadAndWaiting") + + ; removing update registration in favor of a while() in the STATE below + ;registerForUpdate(1) + endEVENT +endSTATE + +STATE deadAndWaiting + + ;-------------------CHANGED FOR DLC2--------------------------- + EVENT onBeginState() + + MQKillDragonScript MQKillDragonS = MQkillDragon as MQKillDragonScript + + ;If in Apocrypha, this is the boss fight so do not wait for distance + if DLC2ApocryphaLocation && DLC2ApocryphaWorld && (game.getPlayer().isInLocation(DLC2ApocryphaLocation) || game.getPlayer().getWorldSpace() == DLC2ApocryphaWorld) + gotoState("deadDisintegrated") + MQkillDragonS.deathSequence(self) + RegisterForSingleUpdateGameTime(0.5) + elseif MQKillDragonS.ShouldMiraakAppear(self) && MiraakAppeared == false + gotoState("deadDisintegrated") +; Debug.Trace(self + "MiraakAppears") + MiraakAppeared = true + MQkillDragonS.deathSequence(self, MiraakAppears = true) + RegisterForSingleUpdateGameTime(0.5) + else + ;NORMAL BASE GAME BEHAVIOR + while getDistance(player) > deathFXrange + utility.wait(1.0) + endWhile +; debug.trace("player close enough to absorb" + self) + gotoState("deadDisintegrated") + MQkillDragonS.deathSequence(self) + RegisterForSingleUpdateGameTime(0.5) + + endif + + endEVENT + ;-------------------------------------------------------------------------------------------- + + ; EVENT onUpdate() + ; if getDistance(player) < deathFXrange +; ; debug.trace("player close enough to absorb" + self) + ; gotoState("deadDisintegrated") + ; unRegisterforUpdate() + ; (MQkillDragon as MQKillDragonScript).deathSequence(self) + ; endif + ;endEVENT +endSTATE + +STATE deadDisintegrated + ; nothing happens here - dead and just bones. + event OnUpdateGameTime() + cell myDragonCell = self.getparentcell() + if !myDragonCell.IsAttached() + NoStalking() + else + RegisterForSingleUpdateGameTime(0.5) + endIf + endEvent +endSTATE + +; _____________ +;| | +;| FUNCTIONS | +;|_____________| +function AnimateFOV(float fFOVfalloff = 1600.0) + ; Function that animates FOV with an ismod. Declaring here in case needed frequently + float playerDist = player.getDistance(self) ; get and store the player's current distance from me + if playerDist < fFOVfalloff + float FOVpower = (1- (1/(fFOVfalloff/(playerDist)))) ; consider offset to compensate for collision size + if FOVpower > 1.0 + FOVpower = 1.0 ; clamp to prevent wacky values + endif +; ;debug.trace("player is " + playerDist + " from landing dragon") +; ;debug.trace("dragon FOV fx power is" + FOVpower) + dragonFOVfx.apply(FOVpower) ;animated FOV effect. Strength based on distance from player + endif +endFunction + +Bool function IsDurnehviir() + + return self.GetActorBase() as form == game.GetFormFromFile(0x000030D8, "Dawnguard.esm") +endFunction + +function NoStalking() + + if self.IsDurnehviir() + return + else + self.DispelAllSpells() + self.SetCriticalStage(self.CritStage_DisintegrateEnd) + endIf +endFunction \ No newline at end of file