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
		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)
		gotoState("deadAndWaiting")
		
		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.
		
		; 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)
		elseif MQKillDragonS.ShouldMiraakAppear(self) && MiraakAppeared == false
			gotoState("deadDisintegrated")
; 			Debug.Trace(self + "MiraakAppears")
			MiraakAppeared = true
			MQkillDragonS.deathSequence(self, MiraakAppears = true)
		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)

		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

; Dragon Stalking Fix v1.2 by sevencardz

; Returns true if this dragon is Durnehviir.
bool Function IsDurnehviir()
	return GetActorBase() == Game.GetFormFromFile(0x0030D8, "Dawnguard.esm")
EndFunction

; Deletes the dragon to prevent the dragon's corpse from stalking the player.
Function NoStalking()
	if IsDurnehviir() ; Durnehviir is not deleted because he never dies.
		;Debug.Trace("DSF IGNORE: " + self + " " + GetBaseObject().GetName() + " is " + GetState())
	else
		DispelAllSpells() ; Remove any lingering magic effect scripts from the dragon.
		Delete() ; Delete the dragon.
		;Debug.Trace("DSF DELETE: " + self + " " + GetBaseObject().GetName() + " is " + GetState())
	endIf
EndFunction

STATE deadDisintegrated

	; Cleans up any dragon corpses which are already stalking the player in the current save game.
	Event OnLoad()
		NoStalking()
	EndEvent

	; Cleans up any dragon corpses in the current area after the player leaves the area.
	Event OnUnload()
		NoStalking()
	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

