scriptName MagicTrap Extends TrapBase
;
;
;===================================================================
;;GENERAL PROPERTIES BLOCK
;===================================================================

spell property magicWeapon auto hidden
;ammo property magicWeaponAmmo auto
float property initialDelay = 0.5 auto hidden
float property firingRate = 2.0 auto hidden
float property currentFiringTimer auto hidden
float property firingSpinup = 0.5 auto hidden
int property castingAim = 0 auto
{use to determin how the aiming type of the magic cast
	0 = straight fire (default)
	1 = fire at linked Ref with TrapLink keyword
	2 = fire at the object that activated my trigger}
objectReference property targetRef auto hidden
bool property forwardFiring = false auto
int property aaSpellToCast = 0 Auto
{The type of spell to cast, the leveled version is resolved internally
	0 = firebolt
	1 = fireball
	2 = flames
	3 = lightning bolt
	4 = chain lightning
	5 = sparks
	6 = ice spike
	7 = frostbite
	8 = ice storm
}
 
import utility
import game
import math

bool finishedFiring = false
bool weaponResolved = false
bool concentrationCastLoop = false
objectReference property mySoulGem auto hidden
formlist property flSoulGemTest auto
int property mySpellLevel = 1  auto hidden
objectReference property trapSelf auto hidden
objectReference property clearRef auto hidden
bool property isFiring auto hidden
bool property isLoaded auto hidden
keyword property TrapLink auto hidden

;===================================================================
;;CORE LOGIC BLOCK
;===================================================================

Function fireTrap()
; 	;;Debug.Trace(self + " is calling fireTrap")
	;if  !trapDisarmed
		;playAnimation("alarm")
		;WindupSound.play( self as ObjectReference)		;play windup sound
		;wait( initialDelay )		;wait for windup
		isFiring = True
		overrideLoop = False
		if !gemTested
			if getLinkedRef().isEnabled()
				testRefIsSoulGem(trapSelf.getLinkedRef())
			
			endif
		endif
		resetLimiter()
		;USLEEP 3.0.11 Bug #14979 - Since the soul gem must be enabled, it must also not be invalid as well so this needed a validity check.
		while(finishedFiring == False) && isLoaded && mySoulGem != None && mySoulGem.isEnabled()
; 			;Debug.Trace(self + " is looping fire")
			;playAnimation("fire")
			if isLoaded && mySoulGem
				wait(firingSpinup)
			endif
			;if statement to handle concentration casting loop
			if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
				if concentrationCastLoop == false
					fireByCastingType()		;does the actual casting
					concentrationCastLoop = True
				endif
			Else
				fireByCastingType()		;does the actual casting
			endif
			finishedFiring = True
			
			;playAnimation("alarm")
			CurrentFiringTimer = (utility.getCurrentRealTime() + firingRate)
			while !overrideLoop && isLoaded && mySoulGem && CurrentFiringTimer > utility.getCurrentRealTime()
				utility.wait(0.3)
			endWhile
			
			if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
				concentrationCastLoop = false
				interruptCast()
			endif
			
			if (loop == TRUE)			;Reset Limiter
				resetLimiter()
			endif
			
		endWhile
		concentrationCastLoop = false
		if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
			interruptCast()
		endif
		
		if isLoaded 	
			isFiring = false
			;if !trapDisarmed
				;playAnimation("idle")
				goToState("Reset")
			;endif
		endif

	;endif

endFunction

state pause
	event onBeginState()
		utility.wait(0.1)
		goToState("disarmed")
	EndEvent
	
	Event OnActivate( objectReference activateRef )
	EndEvent
endState

auto state disarmed
	;;No trap rearming allowed.
	; event onTriggerEnter(ObjectReference triggerRef)
; 		; ;Debug.Trace(self + " has been entered by" + triggerRef)
		; testRefIsSoulGem(triggerRef)
	; endEvent
	event onBeginState()
; 		;Debug.Trace(self + " has entered state Disarmed")
	endEvent
	
	Event OnActivate( objectReference activateRef )
	EndEvent
endState

State Reset

	Event OnBeginState()
; 		;Debug.Trace(self + " has entered state Reset")
		GoToState ( "Idle" )
	endEvent
	
	Event OnActivate( objectReference activateRef )
	EndEvent
	
endState

state Arming
	event onTriggerLeave(objectReference triggerRef)
	endEvent
endState

;===================================================================
;;TRIGGER EVENT BLOCK
;===================================================================

;/
event onTriggerLeave(objectReference triggerRef)
; 	Debug.trace(self + " has been left by " + triggerRef)
	if triggerRef == mySoulGem && AttachSetUpComplete
; 		;Debug.Trace(triggerRef + " == " + mySoulGem)
		
		clearMySoulGem()
		loop = false
		trapDisarmed = True
		goToState("pause")
	; elseif game.getPlayer().getItemCount(mySoulGem)
; 		; ;Debug.Trace(mySoulGem + " is in the player inventory")
		; mySoulGem = clearRef
		; loop = false
		; trapDisarmed = True
		; goToState("pause")
	else
; 		Debug.Trace(triggerRef + " != " + mySoulGem)
	endif
endEvent
/;

;===================================================================
;;LOADED EVENT BLOCK
;===================================================================

;/
event onLoad()
	playAnimation("remove")
	trapSelf = self as objectReference
	isLoaded = TRUE
	if isFiring == True
		fireTrap()
	endif
endEvent
/;

event OnLoad()
; 	Debug.Trace(self + ": onAttachedToCell called")
	isLoaded = TRUE
	trapSelf = self as objectReference	
	
	CellAttachSetUp(trapSelf)
endEvent

;/
event onCellLoad()
; 	;;Debug.Trace(self + " has recieved onCellLoad")
; 	Debug.Trace(self + ": onCellLoad called")
	isLoaded = TRUE
	trapSelf = self as objectReference	
	
	CellAttachSetUp(trapSelf)

endEvent
/;

event onCellDetach()
; 	Debug.Trace(self + " has recieved onDetach event")
	AttachSetUpComplete = FALSE
	isLoaded = FALSE
	gemTested = FALSE
endEvent

event onReset()
; 	debug.Trace(self + ": is running onReset")
	self.reset()

endEvent

;===================================================================
;;FUNCTION BLOCK
;===================================================================

function resetLimiter()
	finishedFiring = false
endFunction

function clearMySoulgem()
	mySoulGem = clearRef
	loop = false
	trapDisarmed = True
	goToState("pause")
endFunction

bool property gemTested = false auto hidden
;test if this is a soul gem
function testRefIsSoulGem(objectReference testSoulGem)
	gemTested = true
	int index = 0
	bool matchFound = false
	while (index < flSoulGemTest.GetSize()) && !matchFound
; 		;;Debug.Trace((testSoulGem) + " testing against " + flSoulGemTest.GetAt(index))
; 		;;Debug.Trace(self + " is checking " + flSoulGemTest + " at index #" + index)
		if testSoulGem.GetBaseObject() == flSoulGemTest.GetAt(index)
; 			;;Debug.Trace(testSoulGem + " == " + flSoulGemTest.GetAt(index))
			(testSoulGem as trapSoulGemController).SetMotionType(4)
			(testSoulGem as trapSoulGemController).goToState("inMagicTrap")
			testSoulGem.moveto(trapSelf, 0.0, 0.0, 128.0)
			
			(testSoulGem as trapSoulGemController).myMagicTrap = trapSelf
			mySpellLevel = index
			resolveLeveledWeapon(index)
			mySoulGem = testSoulGem
			MatchFound = True
			trapDisarmed = False
			goToState("Reset")
			
		endif
		index +=1
	endWhile
endFunction
	
function fireByCastingType()
; 	debug.Trace(self + ": starting fireByCastingType")
	if	castingAim == 0
		magicWeapon.cast(self)
	elseIf castingAim == 1
		if forwardFiring
			if getLinkedRef(TrapLink) && refIsValidForwardTarget(getLinkedRef(TrapLink)) 
				magicWeapon.cast(self, getLinkedRef(TrapLink))
			Else
				magicWeapon.cast(self)
			endif
		Elseif getLinkedRef(TrapLink)
			magicWeapon.cast(self, getLinkedRef(TrapLink))
		Else
			magicWeapon.cast(self)
		endif
	ElseIf castingAim == 2
		targetRef = (lastActivateRef as traptriggerbase).lastTriggerRef
		if forwardFiring
			if refIsValidForwardTarget(targetRef)
				magicWeapon.cast(self, targetRef)
			Else
				magicWeapon.cast(self)
			endif
		Else
			magicWeapon.cast(self, targetRef)
		endif
	Else
; 		Debug.Trace(self + " error: castingAim = " + castingAim)
	endif
; 	debug.Trace(self + ": finished fireByCastingType")
endFunction

;;This section is for determining if the target is withing a valid firing angle
;		it is used for magic traps that fire forward only
float property horizontalSweepHalf = 90.0 auto
float property verticalSweepHalf = 90.0 auto

bool function refIsValidForwardTarget(objectReference testRef)
; 	;Debug.Trace(self + " is checking if refIsValidForwardTarget on " + testRef)
	float deltaX = testRef.X - self.X
	float deltaY = testRef.Y - self.Y
	float deltaZ = testRef.Z - self.Z
	
	float fAngleToRef = atan(deltaX/deltaY)
	if (deltaX < 0.0) && (deltaY < 0.0)
		fAngleToRef = fAngleToRef + 180
	endIf
	float deltaAngle = self.getAngleZ()-fAngletoRef
	if (deltaAngle < -180.0)
		deltaAngle = deltaAngle + 360.0
	endIf
	if (deltaAngle > 180.0)
		deltaAngle = deltaAngle - 360.0
	endIf
	
	if Abs(deltaAngle) < horizontalSweepHalf
; 		;;Debug.Trace(self + " is firing at a valid angle at " + testRef)
		return true
	Else
; 		;;Debug.Trace(self + " could not fire at a valid angle at " + testRef)
		return False
	endif
endFunction

bool property AttachSetUpComplete = false auto hidden

Function CellAttachSetUp(objectReference functionTriggerRef)
{This function is used to handshake between the trap and the soulgem}

	;USKP 2.0.2 - If no linked ref, bail out. Some of these don't have one.
	if( GetLinkedRef() == None )
		Return
	EndIf
	
; 	debug.Trace(self + ": has been caused to run CellAttachSetUp by " + functionTriggerRef)
	if !AttachSetUpComplete
; 		debug.Trace(self + ": has found that AttachSetUpComplete == false")
		if isLoaded && getLinkedRef().isEnabled()
			trapSoulGemController localGemScript
			localGemScript = getLinkedRef() as trapSoulgemController
		
			if localGemScript.getState() != "Disarmed"
; 				debug.Trace(self + ": is running CellAttachSetup")
				AttachSetUpComplete = True
				goToState("Idle")
				if !gemTested
					if getLinkedRef().isEnabled()
						testRefIsSoulGem(trapSelf.getLinkedRef())
					
					endif
				endif
				if isFiring == True
					fireTrap()
				endif
			else
; 				debug.Trace(self + ": has found that my gem is in state disarmed")
			endif
		else
; 			debug.Trace(self + ": has found that is loaded is false or linkedRef is not enabled")
		endif
	else
; 		debug.Trace(self + ": has found that AttachSetUpComplete == true")
	endif
endFunction


;===================================================================
;;DAMAGE LEVELLING BLOCK
;===================================================================

int property TESTLvlEncValue auto
int property LvlThreshold1 auto
spell property LvlWeapon1 auto hidden
int property LvlThreshold2 auto
spell property LvlWeapon2 auto hidden
int property LvlThreshold3 auto
spell property LvlWeapon3 auto hidden
int property LvlThreshold4 auto
spell property LvlWeapon4 auto hidden
int property LvlThreshold5 auto
spell property LvlWeapon5 auto hidden
spell property LvlWeapon6 auto hidden

bool spellAndCastingTypeResolved = false

;
Function ResolveLeveledWeapon (int spellLevel)
; 	;;Debug.Trace(self + " trying to ResolveLeveledWeapon")
	if spellAndCastingTypeResolved == false
		ResolveSpellType()
		ResolveCastingDelay()
		spellAndCastingTypeResolved = True
	endif
	;int damageLevel
	;damageLevel = CalculateEncounterLevel(TrapLevel)
	spellLevel = CalculateEncounterLevel(TrapLevel)
	spell lvlWeapon = LvlWeapon1
	
	if (spellLevel == 2)
		lvlWeapon = LvlWeapon2
	elseif (spellLevel == 3)
		lvlWeapon = LvlWeapon3
	elseif (spellLevel == 4)
		lvlWeapon = LvlWeapon4
	elseif (spellLevel == 5)
		lvlWeapon = LvlWeapon5
	elseif (spellLevel > 5)
		lvlWeapon = LvlWeapon6
	endif
	magicWeapon = lvlWeapon
EndFunction

function ResolveCastingDelay()
; 	;Debug.Trace(self + " trying to ResolveCastingDelay")
	if castingAim == 0
		initialDelay = 0.5
		if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
			firingRate = 4.5
		else
			firingRate = 2.0
		endif
	elseif castingAim == 1
		initialDelay = 0.5
		if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
			firingRate = 4.5
		else
			firingRate = 2.0
		endif
	elseif castingAim == 2
		initialDelay = 0.5
		if aaSpellToCast == 2 || aaSpellToCast == 5 || aaSpellToCast == 7
			firingRate = 4.5
		else
			firingRate = 2.0
		endif
	Else
; 		;Debug.Trace(self + " cannot resolveCastingDelay: unknown castingAim")
	endIf
		
endFunction

function ResolveSpellType()
; 	;;Debug.Trace(self + " trying to ResolveSpellType")
	if	aaSpellToCast == 0
		LvlWeapon1 = sLvlfirebolt1
		LvlWeapon2 = sLvlfirebolt2
		LvlWeapon3 = sLvlfirebolt3
		LvlWeapon4 = sLvlfirebolt4
		LvlWeapon5 = sLvlfirebolt5
		LvlWeapon6 = sLvlfirebolt6
	elseif aaSpellToCast == 1
		LvlWeapon1 = sLvlfireball1
		LvlWeapon2 = sLvlfireball2
		LvlWeapon3 = sLvlfireball3
		LvlWeapon4 = sLvlfireball4
		LvlWeapon5 = sLvlfireball5
		LvlWeapon6 = sLvlfireball6
	elseif aaSpellToCast == 2
		LvlWeapon1 = sLvlFlames1
		LvlWeapon2 = sLvlFlames2
		LvlWeapon3 = sLvlFlames3
		LvlWeapon4 = sLvlFlames4
		LvlWeapon5 = sLvlFlames5
		LvlWeapon6 = sLvlFlames6
	elseif aaSpellToCast == 3
		LvlWeapon1 = sLvlLightningBolt1
		LvlWeapon2 = sLvlLightningBolt2
		LvlWeapon3 = sLvlLightningBolt3
		LvlWeapon4 = sLvlLightningBolt4
		LvlWeapon5 = sLvlLightningBolt5
		LvlWeapon6 = sLvlLightningBolt6
	elseif aaSpellToCast == 4
		LvlWeapon1 = sLvlChainLightning1
		LvlWeapon2 = sLvlChainLightning2
		LvlWeapon3 = sLvlChainLightning3
		LvlWeapon4 = sLvlChainLightning4
		LvlWeapon5 = sLvlChainLightning5
		LvlWeapon6 = sLvlChainLightning6
	elseif aaSpellToCast == 5
		LvlWeapon1 = sLvlSparks1
		LvlWeapon2 = sLvlSparks2
		LvlWeapon3 = sLvlSparks3
		LvlWeapon4 = sLvlSparks4
		LvlWeapon5 = sLvlSparks5
		LvlWeapon6 = sLvlSparks6
	elseif aaSpellToCast == 6
		LvlWeapon1 = sLvlIceSpike1
		LvlWeapon2 = sLvlIceSpike2
		LvlWeapon3 = sLvlIceSpike3
		LvlWeapon4 = sLvlIceSpike4
		LvlWeapon5 = sLvlIceSpike5
		LvlWeapon6 = sLvlIceSpike6
	elseif aaSpellToCast == 7
		LvlWeapon1 = sLvlFrostBite1
		LvlWeapon2 = sLvlFrostBite2
		LvlWeapon3 = sLvlFrostBite3
		LvlWeapon4 = sLvlFrostBite4
		LvlWeapon5 = sLvlFrostBite5
		LvlWeapon6 = sLvlFrostBite6
	elseif aaSpellToCast == 8
		LvlWeapon1 = sLvlIceStorm1
		LvlWeapon2 = sLvlIceStorm2
		LvlWeapon3 = sLvlIceStorm3
		LvlWeapon4 = sLvlIceStorm4
		LvlWeapon5 = sLvlIceStorm5
		LvlWeapon6 = sLvlIceStorm6
	else
; 		;Debug.Trace(self + " has been set to an invalid spell type")
	endif
endFunction

spell property sLvlfirebolt1 auto
spell property sLvlfirebolt2 auto
spell property sLvlfirebolt3 auto
spell property sLvlfirebolt4 auto
spell property sLvlfirebolt5 auto
spell property sLvlfirebolt6 auto

spell property sLvlfireball1 auto
spell property sLvlfireball2 auto
spell property sLvlfireball3 auto
spell property sLvlfireball4 auto
spell property sLvlfireball5 auto
spell property sLvlfireball6 auto

spell property sLvlFlames1 auto
spell property sLvlFlames2 auto
spell property sLvlFlames3 auto
spell property sLvlFlames4 auto
spell property sLvlFlames5 auto
spell property sLvlFlames6 auto

spell property sLvlLightningBolt1 auto
spell property sLvlLightningBolt2 auto
spell property sLvlLightningBolt3 auto
spell property sLvlLightningBolt4 auto
spell property sLvlLightningBolt5 auto
spell property sLvlLightningBolt6 auto

spell property sLvlChainlightning1 auto
spell property sLvlChainlightning2 auto
spell property sLvlChainlightning3 auto
spell property sLvlChainlightning4 auto
spell property sLvlChainlightning5 auto
spell property sLvlChainlightning6 auto

spell property sLvlSparks1 auto
spell property sLvlSparks2 auto
spell property sLvlSparks3 auto
spell property sLvlSparks4 auto
spell property sLvlSparks5 auto
spell property sLvlSparks6 auto

spell property sLvlIceSpike1 auto
spell property sLvlIceSpike2 auto
spell property sLvlIceSpike3 auto
spell property sLvlIceSpike4 auto
spell property sLvlIceSpike5 auto
spell property sLvlIceSpike6 auto

spell property sLvlFrostbite1 auto
spell property sLvlFrostbite2 auto
spell property sLvlFrostbite3 auto
spell property sLvlFrostbite4 auto
spell property sLvlFrostbite5 auto
spell property sLvlFrostbite6 auto

spell property sLvlIceStorm1 auto
spell property sLvlIceStorm2 auto
spell property sLvlIceStorm3 auto
spell property sLvlIceStorm4 auto
spell property sLvlIceStorm5 auto
spell property sLvlIceStorm6 auto

;Event OnTrapHit(ObjectReference akTarget, float afXVel, float afYVel, float afZVel, float afXPos, float afYPos, float afZPos, int aeMaterial, bool abInitialHit, int aeMotionType)
;	if (hitOnce == FALSE)
;		hitOnce = TRUE
;	endif
;endEvent