###########################################################
# Special Effects - Railshooter Module
# Contains Classes for Texture Map Movies and Non-Particle Special FX
###########################################################

# DESCRIPTION:
# This file contains Classes to create various non-particle based 
# effects involving flat textures, such as texture-map movie 
# explosions.  The specific classes work as follows:

# Class Explosion - Creates one animated texture-map movie explosion 
# at the specified coordinates and scale.

# Class ChainExplosion - Creates either a series of delayed Explosions 
# in a cluster (standard enemy explosion effect) or creates a series 
# of delayed Explosions in the same target (used for animated explosion 
# trails)

# Class SpriteShockwave - Creates one plane with a non-animated 
# texture-map that grows in scale and transparency over time (used for
# Homing Laser explosion effect)

# Class ChainSpriteShockwave - Creates either a series of delayed 
# SpriteShockwaves in a cluster or creates a series of delayed 
# SpriteShockwaves on the same target (used for green missile trails)


from direct.showbase.ShowBaseGlobal import *
from direct.interval.IntervalGlobal import *
from direct.showbase import DirectObject
from direct.task import Task
from pandac.PandaModules import *
import math, random

import ClassGlobals
import Level1
import LoadTextures
import SoundFX


########################################
# Chain Explosion Class
########################################
class ChainExplosion(DirectObject.DirectObject):
    def __init__(self, numExplosions, variance, explosionSpeed, explosionScale, target, trail):

        # Define Variables
        self.numExplosions = numExplosions
        self.variance = variance
        self.explosionSpeed = explosionSpeed
        self.explosionScale = explosionScale
	self.target = target
	self.trail = trail
        self.targetX = self.target.getX(Level1.railDummy)
        self.targetY = self.target.getY(Level1.railDummy)
        self.targetZ = self.target.getZ(Level1.railDummy)
	
        self.currentExplosions = 0
        self.currentExplosionDelay = 0

	# Add the Task to the Task Manager
	taskMgr.add(self.animateExplosion, 'ChainExplosion')


    ########################################
    # Define Tasks
    ########################################
    def animateExplosion(self, task):

	# If cooldown time done, shoot!
	if task.time >= self.currentExplosionDelay:

	    # If Trail, then get new location 
	    if self.trail == 1:
		self.targetX = self.target.getX(Level1.railDummy)
        	self.targetY = self.target.getY(Level1.railDummy)
        	self.targetZ = self.target.getZ(Level1.railDummy)

	    self.currentExplosions += 1
		    
            x = random.uniform(-self.variance, self.variance) + self.targetX
            y = random.uniform(-self.variance, self.variance) + self.targetY
            z = random.uniform(-self.variance, self.variance) + self.targetZ
            scale = random.uniform(-self.variance, self.variance) + self.explosionScale

	    # Create Explosion
            self.kaboom = Explosion(x, y, z, scale)

 	    # If still shots left to fire, restore Cooldown Time
	    if self.numExplosions > self.currentExplosions:
		self.currentExplosionDelay += self.explosionSpeed + random.uniform(0, 0.05)

	# If all done with explosions, kill Task
	if self.currentExplosions >= self.numExplosions:
	    return Task.done
	else:
	    return Task.cont

    
########################################
# Explosion Class
########################################
class Explosion(DirectObject.DirectObject):
    def __init__(self, x, y, z, scale):   

	# Define Variables
	self.framecount = 0  		#Holds Current Texture Map Frame
	self.currentTime = 0		# Holds the Current Time
	self.animationRate = 0.01  	# Texture Map Movie Animation Rate
	self.animationDecel = 0.0005    # Deceleration Rate for Animation
	self.numFrames = LoadTextures.numFrames		# Number of Frames in the Animation Sequence

	# Load a plane (plane model not normally included with Panda)
	self.explosionCard = loader.loadModelCopy('Models/basics/plane')
	self.explosionCard.setHpr(0, 90, 0)
	self.explosionCard.setTransparency(1)	
	self.explosionCard.setScale(scale, scale, scale)
	self.explosionCard.reparentTo(Level1.railDummy)
	self.explosionCard.setPos(Level1.railDummy, x, y, z)

	# Set Explosion to be Unaffected by Lighting
	lightAttributes = LightAttrib.makeAllOff()
	self.explosionCard.node().setAttrib(lightAttributes)

	# Load frame 0 
	self.explosionCard.setTexture(LoadTextures.explosionMap[self.framecount], 1)

	# Add the Task to the Task Manager
	taskMgr.add(self.animateExplosion, 'TextureMapMovie')

	# Play Sound Effect
	SoundFX.explosion3.start()


    ########################################
    # Define Tasks
    ########################################

    # Create a Task to Animate the Explosion
    def animateExplosion(self, task):

    	# Calculate the Current Time, rounded to 1/10 of a second
    	taskTime = math.floor(task.time / self.animationRate)

    	# If ready for a texture swap, do so
    	if self.currentTime < taskTime:
	    self.currentTime = taskTime
	    self.animationRate += self.animationDecel
	
	    # Change to a New Texture Map
	    self.framecount += 1
	    if self.framecount >= self.numFrames:
	        self.destroy()
	        return Task.done
	    else:
		self.explosionCard.setTexture(LoadTextures.explosionMap[self.framecount], 1)
   
        # Make the Task Continue
        return Task.cont

    # Destroys the Explosion Effect
    def destroy(self):
	self.explosionCard.removeNode()



########################################
# Chain Sprite Shockwave Class
########################################
class ChainSpriteShockwave(DirectObject.DirectObject):
    def __init__(self, spriteType, numExplosions, variance, explosionSpeed, explosionScale, target, shockwaveSpeed, trail):

        # Define Variables
	self.spriteType = spriteType
        self.numExplosions = numExplosions
        self.variance = variance
        self.explosionSpeed = explosionSpeed
        self.explosionScale = explosionScale
	self.target = target
	self.shockwaveSpeed = shockwaveSpeed
	self.trail = trail
        self.targetX = self.target.getX(Level1.railDummy)
        self.targetY = self.target.getY(Level1.railDummy)
        self.targetZ = self.target.getZ(Level1.railDummy)
	
        self.currentExplosions = 0
        self.currentExplosionDelay = 0

	# Add the Task to the Task Manager
	taskMgr.add(self.animateExplosion, 'ChainExplosion')


    ########################################
    # Define Tasks
    ########################################
    def animateExplosion(self, task):

	# Destroy Task if Projectile Destroyed
	if self.target.isEmpty():
	    return Task.done

	# If cooldown time done, shoot!
	if task.time >= self.currentExplosionDelay:

	    # If Trail, then get new location 
	    if self.trail == 1:
		self.targetX = self.target.getX(Level1.railDummy)
        	self.targetY = self.target.getY(Level1.railDummy)
        	self.targetZ = self.target.getZ(Level1.railDummy)

	    self.currentExplosions += 1
		    
            x = random.uniform(-self.variance, self.variance) + self.targetX
            y = random.uniform(-self.variance, self.variance) + self.targetY
            z = random.uniform(-self.variance, self.variance) + self.targetZ
            scale = random.uniform(-self.variance, self.variance) + self.explosionScale
	
	    # Create the Shockwave
	    if self.spriteType == 'smoke':
	        self.kaboom = SpriteShockwave(self.spriteType, x, y, z, scale, scale*2, self.shockwaveSpeed)
	    else:
		self.kaboom = SpriteShockwave(self.spriteType, x, y, z, scale/4, scale*4, self.shockwaveSpeed)

 	    # If still shots left to fire, restore Cooldown Time
	    if self.numExplosions > self.currentExplosions:
		self.currentExplosionDelay += self.explosionSpeed + random.uniform(0, 0.05)

	# If all done with explosions, kill Task
	if self.currentExplosions >= self.numExplosions:
	    return Task.done
	else:
	    return Task.cont


########################################
# Shockwave Class
########################################
class SpriteShockwave(DirectObject.DirectObject):
    def __init__(self, spriteType, x, y, z, scale, maxScale, scaleSpeed):   

	# Load a plane (plane model not normally included with Panda)
	self.shockPlane = loader.loadModelCopy('Models/basics/plane')
	self.shockPlane.setHpr(0, 90, 0)
	self.shockPlane.setTransparency(1)
	self.shockPlane.setScale(scale, scale, scale)
	self.shockPlane.reparentTo(Level1.railDummy)
	self.shockPlane.setPos(Level1.railDummy, x, y, z)

	# Set Explosion to be Unaffected by Lighting
	lightAttributes = LightAttrib.makeAllOff()
	self.shockPlane.node().setAttrib(lightAttributes)

	# Load Texture map
	if spriteType == 'shockwave':
	    self.shockPlane.setTexture(LoadTextures.shockwaveMap, 1)

            # Animate the Projectile
            self.rotateBlast = self.shockPlane.hprInterval(1.0, 
	    		Point3(0, 90, 180), 
	    		startHpr=Point3(0, 90, 0))
    	    self.rotateBlast.loop()

            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')


	elif spriteType == 'plasma':
	    self.shockPlane.setTexture(LoadTextures.plasmaMap, 1)

            # Animate the Projectile
            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')


	elif spriteType == 'radiation':
	    self.shockPlane.setTexture(LoadTextures.radMap, 1)

            # Animate the Projectile
            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')


	elif spriteType == 'pulser':
	    self.shockPlane.setTexture(LoadTextures.pulseMap, 1)

            # Animate the Projectile
            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')

	    # Reparent Effect to Player
	    self.shockPlane.reparentTo(ClassGlobals.p1.player)
	    self.shockPlane.setPos(ClassGlobals.p1.player, 0, 0, 2)


	elif spriteType == 'powerup':
	    self.shockPlane.setTexture(LoadTextures.powerupShockwave, 1)

            # Animate the Projectile
            self.rotateBlast = self.shockPlane.hprInterval(1.0, 
	    		Point3(0, 90, 180), 
	    		startHpr=Point3(0, 90, 0))
    	    self.rotateBlast.loop()

            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')


	elif spriteType == 'smoke':
	    self.shockPlane.setTexture(LoadTextures.explosionMap[19], 1)

            # Animate the Projectile
            self.rotateBlast = self.shockPlane.hprInterval(4.0, 
	    		Point3(0, 90, 360), 
	    		startHpr=Point3(0, 90, 0))
    	    self.rotateBlast.loop()

            self.scaleBlast = self.shockPlane.scaleInterval(scaleSpeed, 
	    		Point3(maxScale, maxScale, maxScale), 
			blendType='easeIn')


        self.colorBlast = self.shockPlane.colorInterval(scaleSpeed, 
	    		Vec4(1, 1, 1, 0), 
			blendType='easeIn')
	self.colorBlast.start()

	self.effectSequence = Sequence(self.scaleBlast, 
				Func(self.destroy))

	self.effectSequence.start()


    ########################################
    # Define Tasks
    ########################################

    # Destroys the Explosion Effect
    def destroy(self):
	self.shockPlane.removeNode()
