###########################################################
# EnemyProjectile - Railshooter Module
# Contains the class to create enemy bullets
###########################################################

# DESCRIPTION:
# This Class creates a Hostile Projectile at the enemy's starting
# position.  The projectile then travels forward in a straight line
# slowly over time, colliding with any Players in the way.  The 
# projectile will auto-destruct over time and can take various forms
# depending on the bulletType string passed in when it is constructed.
# The Enemy Projectile will also collide with the Player's Projectiles,
# allowing the player to intercept enemy bullets.


from direct.showbase.ShowBaseGlobal import *
from direct.interval.IntervalGlobal import *
from direct.showbase import DirectObject
from direct.task import Task
import random

# Import Other Railshooter Modules
import ClassGlobals
import Level1
import SoundFX
import specialEffects
import LoadTextures


########################################
# Enemy Projectile Class
########################################
class EnemyProjectile(DirectObject.DirectObject):
    def __init__(self, bulletType, bulletNum, enemyPos, enemyHpr, accuracy, p1):   
	
	self.bulletType = bulletType
	self.accuracy = accuracy
	self.collider = ClassGlobals.collider
	self.bulletNum = str(bulletNum)
	self.p1 = p1

	########################################
	# Initialize Variables
	########################################
	self.gameSpeed = ClassGlobals.gameSpeed

	self.hasCollided = 0
	self.startPos = enemyPos
	self.startHpr = enemyHpr
	
	self.tick = 0
	self.currentTick = 0

	self.bulletDamage = 1.0


	########################################
	# Load the Projectile Model
	########################################

	# Dummy object for the bullet to keep track of its starting
	# Pos and Orientation
	self.anchor = render.attachNewNode('anchor' + self.bulletNum)
	self.anchor.reparentTo(Level1.railDummy)

     	# Add a dummy object for the projectile
	# This object is the one that gets moved around
	self.bullet = self.anchor.attachNewNode('bullet' + self.bulletNum)

	# Choose Which Projectile to Create
	if self.bulletType == 'fire':

            # Load the Fire Projectile Model
 	    self.shell = loader.loadModelCopy("Models/effects/circleProjectile")
	    self.shell.setScale(0.3, 0.3, 0.3)
            self.shell.setHpr(180, 0, 0)
     	    self.shell.reparentTo(self.bullet)

	    # Set Projectile Stats
	    self.speed = 1.0
	    self.points = 50
	    self.lifespan = 400
  
            # Animate the Projectile
            self.rotateBlast = self.shell.hprInterval(0.3, 
	    		Point3(360, 0, 0), 
	    		startHpr=Point3(0, 0, 0))
    	    self.rotateBlast.loop()

	    # Create Bullet's Collision Sphere
            self.bulletSphere = CollisionSphere(0, 0, 0, 3)
            self.enemycollbullet = CollisionNode("enemycollbullet" + self.bulletNum)
            self.enemycollbullet.addSolid(self.bulletSphere)
            self.collision = self.bullet.attachNewNode(self.enemycollbullet)
            # self.collision.show()


	elif self.bulletType == 'plasma':
            # Load the Plasma Projectile Model
 	    self.shell = loader.loadModelCopy("Models/effects/plasmaShot5")
	    self.shell.setTexture(LoadTextures.greenProjectile, 1)
	    self.shell.setScale(0.4, 0.6, 0.4)
            self.shell.setHpr(180, 0, 0)
     	    self.shell.reparentTo(self.bullet)

	    # Set Projectile Stats
	    self.speed = 2.0
	    self.points = 100	
	    self.lifespan = 300

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

	    # Create Bullet's Collision Sphere
            self.bulletSphere = CollisionSphere(0, 1.0, 0, 2.5)
            self.enemycollbullet = CollisionNode("enemycollbullet" + self.bulletNum)
            self.enemycollbullet.addSolid(self.bulletSphere)
            self.collision = self.bullet.attachNewNode(self.enemycollbullet)
            # self.collision.show()

	    # Create Effect Trail
	    self.effectTrail = specialEffects.ChainSpriteShockwave('shockwave', 10, 0, 0.8, 7, self.shell, 1, 1)


	# Create a Blank Light Attribute
	# Results in Object being unaffected by light and
	# glowing as a result
	lightAttributes = LightAttrib.makeAllOff()
	self.shell.node().setAttrib(lightAttributes)

	# Set Bitmask for the Bullet
	# Targets need Bitmask 0 to Register a Hit
	self.enemycollbullet.setFromCollideMask(BitMask32.bit(0))

	# Victims with Bitmask 1 Collide with Bullet
	self.enemycollbullet.setIntoCollideMask(BitMask32.bit(1))

	# Turn on collisions
	self.collider.enemyBulletTraverser.addCollider(self.collision, self.collider.collisionEvent)

	# Set the Starting Anchor Point
	self.anchor.setPos(self.startPos)
	self.anchor.setHpr(self.startHpr)

	# Aim the Projectile at the player
	self.anchor.lookAt(self.p1.player)
	
	# Add Random Noise to Enemy's Aim
	aH = self.anchor.getH()
	aP = self.anchor.getP()
	aR = self.anchor.getR()

	aH += random.uniform(-self.accuracy, self.accuracy)/10.0
	aP += random.uniform(-self.accuracy, self.accuracy)/10.0
	aR += random.uniform(-self.accuracy, self.accuracy)/10.0

	self.anchor.setHpr(aH, aP, aR)


	########################################
	# Accept Events
	########################################

	self.accept('enterenemycollbullet' + self.bulletNum, self.explode)


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

    # Create a task to Move the Projectile
    def moveProjectile(self, task):

	# If Projectile has not Collided yet
	if self.hasCollided == 0:

	    # Calculate the Current Tick (Game Time)
	    # This makes the game controls independent of Framerate
	    self.currentTick = math.floor(task.time / self.gameSpeed)

	    # If it is a Tick, then Update the World
	    if self.currentTick > self.tick:
	        self.tick = self.currentTick

  	        # Get Projectile's Coordinates
                pY = self.bullet.getY()
	        pY = pY + self.speed

	        # Set New Player Location & Orientation
	        self.bullet.setY(self.anchor, pY)

	   # If the Projectile lasts over its lifespan, destroy it
	    if self.tick > self.lifespan or ClassGlobals.gameIsOver == 1:
		# End the Task
		self.destroy()
		return Task.done
	    else:
		# Make the task continue
		return Task.cont

	# Else, Projectile is Dead, so End Task
	else:
	    return Task.done


    # Cause Bullet to Explode
    def explode(self, collisionEntry):
        
        # Don't Do this if Already Destroyed
	if self.hasCollided == 0:
            # Play Sound Effect
            SoundFX.explosion2.start()

	    # Add Point Scoring
	    ClassGlobals.p1.score += self.points

            # Play Explosion Effect
            self.kaboom = specialEffects.ChainExplosion(1, 0, 0.01, 7, self.bullet, 0)

            # Remove Bullet and Components
            self.destroy()


    # Destroy the Projectile and Remove its Parts
    def destroy(self):

	# The Bullet has Collided (Mark for Destruction)
	self.hasCollided = 1

	self.collider.enemyBulletTraverser.removeCollider(self.collision)
  	self.collision.removeNode()
	self.shell.removeNode()
	self.bullet.removeNode()
	self.anchor.removeNode()







