###########################################################
# Enemy Homing Missile - Railshooter Module
# Creates a projectile that slowly homing in on targets
###########################################################

# DESCRIPTION:
# This Class is used to create a Hostile Homing Missile.  These
# Projectiles automatically adjust their trajectory to always 
# aim at the player and cannot be out-run.  They can be shot down
# by the player's bullets however.  

# This file defines the statistics of the bullet, loads the models and
# effects, moves the homing bullet, and resolves collisions.  Also, 
# the Homing Missile, like all projectiles, will Auto-Destruct over time.


from direct.showbase.ShowBaseGlobal import *
from direct.showbase import DirectObject
import random

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


########################################
# Enemy Homing Missile Class
########################################
class EnemyHomingMissile(DirectObject.DirectObject):
    def __init__(self, bulletType, bulletNum, playerPos, playerHpr, target):   

	self.bulletType = bulletType
	self.collider = ClassGlobals.collider
	self.bulletNum = str(bulletNum)
	self.target = target

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

	self.hasCollided = 0
	self.points = 100	

	self.speed = random.uniform(1, 20)
	self.acceleration = 0.3
	self.lifespan = 400
	self.turningSpeed = 1
	self.turningAcceleration = 0

	self.startPos = playerPos
	self.startHpr = playerHpr
	
	self.tick = 0
	self.currentTick = 0

	self.prevT = 0
	self.prevX = 0
	self.prevY = 0
	self.prevZ = 0


	########################################
	# Load the Homing Missile 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)

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

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

	# Create Effect Trail
	self.effectTrail = specialEffects.ChainSpriteShockwave('smoke', 12, 0, 1.0, 3, self.shell, 1, 1)
 
        # Create Player's Collision Sphere
        self.bulletSphere = CollisionSphere(0, 1.0, 0, 2.5)
        self.enemyHomingBullet = CollisionNode("enemyHomingBullet" + self.bulletNum)
        self.enemyHomingBullet.addSolid(self.bulletSphere)
        self.collision = self.bullet.attachNewNode(self.enemyHomingBullet)
        # self.collision.show()

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

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

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

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


	########################################
	# Play Firing Sounds
	########################################

	# Play Sound Effect
	SoundFX.eMissile.start()


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

	self.accept('enterenemyHomingBullet' + self.bulletNum, self.checkCollision)


    ########################################
    # 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 Change in Time
 	    	dt = task.time - self.prevT
            	self.prevT = task.time

		# Alter Projectile Facing To Aim More At Target
		self.absoluteFace(self.target)
		
  	        # Get Projectile's New Speed
		self.speed += self.acceleration
		
		# Get New Turning Speed
		self.turningSpeed += self.turningAcceleration

	        # Set New Projectile Location & Orientation
	        self.bullet.setY(self.bullet, dt*self.speed)

	   # If the Projectile lasts over 10 seconds, 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


    # Immediately Face Target
    def absoluteFace(self, target):
	self.bullet.lookAt(self.target)


    # Alters the facing of the missile to slowly aim at the target
    def faceTarget(self, target):

	# Get Bullet's Current Facing
	bulletH = self.bullet.getH()
	bulletP = self.bullet.getP()
	bulletR = self.bullet.getR()

	# Get Exact Correct Facing towards the Target
	self.bullet.lookAt(self.target)
	goodH = self.bullet.getH()
	goodP = self.bullet.getP()
	goodR = self.bullet.getR()

	# Use the Current and Optimal Facing Data to Slowly
	# Adjust the Bullet's Facing Over Time
	if goodH > 0:
	    if bulletH + self.turningSpeed >= goodH:
	 	bulletH = goodH
	    else:
	     	bulletH = bulletH + self.turningSpeed
	elif goodH < 0:
	    if bulletH - self.turningSpeed <= goodH:
		bulletH = goodH
	    else:
	   	bulletH = bulletH - self.turningSpeed

	if goodP > 0:
    	    if bulletP + self.turningSpeed >= goodP:
	 	bulletP = goodP
	    else:
	    	bulletP = bulletP + self.turningSpeed
	elif goodP < 0:
    	    if bulletP - self.turningSpeed <= goodP:
	 	bulletP = goodP
	    else:
	     	bulletP = bulletP - self.turningSpeed

	if goodR > 0:
    	    if bulletR + self.turningSpeed >= goodR:
	 	bulletR = goodR
	    else:
	     	bulletR = bulletR + self.turningSpeed
	elif goodR < 0:
    	    if bulletR - self.turningSpeed <= goodR:
	 	bulletR = goodR
	    else:
	     	bulletR = bulletR - self.turningSpeed

	# Set New Bullet Facing
	self.bullet.setHpr(bulletH, bulletP, bulletR)


    # Check whether to Accept the Collision
    def checkCollision(self, collisionEntry):

	# Get the Victim's Name
	victim = collisionEntry.getFromNode()
	victim = victim.getName()

	# Don't explode if Collided with Player
	if victim[0:10] == 'collplayer' or victim[0:10] == 'collbullet':
	    self.explode()


    # Cause Bullet to Explode
    def explode(self):
        
        # 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, 5, 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.enemyMissileTraverser.removeCollider(self.collision)
 	self.collision.removeNode()
	self.shell.removeNode()
	self.bullet.removeNode()
	self.anchor.removeNode()

