###########################################################
# Level1 - Railshooter Module
# Loads the First Level of the Game
###########################################################

# DESCRIPTION:
# This is the first Level file.  It loads the level, defines the 
# level's boundaries, and determines when the enemy formations appear.  
# It also calls the functions to create lighting and fog for the world.
# This Class also contains many Dummy Objects that are used for 
# moving the craft along the rail and for sorting Particle Effects.


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

# Load Railshooter Modules
import EnemyFormations
import ClassGlobals
from direct.directutil import Mopath
import LoadMusic
import LoadLighting
import SoundFX


########################################
# Define Variables
########################################

# Sets the Maximum Boundaries for the Player Craft's Movement
maxBoundX = 86
maxBoundNegX = 90
maxBoundZ = 24
maxBoundNegZ = 60


########################################
# Load the Rail Dummy
########################################

# Add a dummy object to follow the Rail
# This object moves the players and triggers events
# This dummy is the anchor for enemy placement
railDummy = render.attachNewNode('railDummy')

# Position the Raildummy
railDummy.setPos(0, 0, 30)

# Used as Reference to get Ground Coordinates
groundDummy = render.attachNewNode('groundDummy')


########################################
# Particle Dummy 
########################################
# Particles should be attached to this node
# in order to be relative to the RailDummy and still
# be unaffected by lighting.  Also, objects attached
# to this Node will be drawn in front of everything 
# else in the world.

unlitDummy = railDummy.attachNewNode('unlitDummy')

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

# Second Unlit Dummy reparent to Render
renderUnlitDummy = render.attachNewNode('renderUnlitDummy')
lightAttributes = LightAttrib.makeAllOff()
renderUnlitDummy.node().setAttrib(lightAttributes)

# Create a GUI Layer Bin for the UnlitDummy
# This forces all particles attached to the UnlitDummy to be 
# drawn ontop of everything else.  Fixes Alpha Problems.
CullBinManager.getGlobalPtr().addBin('gui-popup', 1, 40)
renderUnlitDummy.setBin('gui-popup', 0)
renderUnlitDummy.setDepthTest(0)


########################################
# Level Class
########################################
class Level1(DirectObject.DirectObject):
    def __init__(self):   

	# Junk Variables
	self.formCounter = 0


   	########################################
    	# Load the Environment Models
    	########################################

    	self.city = loader.loadModelCopy('Models/city/SuperCity')
    	self.city.reparentTo(render)

    	# NOTE:  Do not Rescale the city because the Rail will
    	#	 not Rescale with it!!!
    	
    	self.skybox = loader.loadModelCopy('Models/skybox/sunsetSkybox2')
    	self.skybox.setScale(4)
    	self.skybox.reparentTo(render)

    	# Create the Light Attribute
	lightAttributes = LightAttrib.makeAllOff()

	# Create a White Ambient Light
	self.ambientLight = AmbientLight("ambientLight")
	self.ambientLight.setColor(Vec4( 2.0, 2.0, 2.0, 1 ))
	lightAttributes = lightAttributes.addLight(self.ambientLight)
	self.skybox.attachNewNode(self.ambientLight.upcastToPandaNode()) 

        # Enable Lighting
	self.skybox.node().setAttrib(lightAttributes)

    	# Make the Skybox Rotate
	self.rotateSky = self.skybox.hprInterval(200, 
				Point3(360, 0, 0), 
				startHpr=Point3(0, 0, 0))
	self.rotateSky.loop()

        # Set the far clipping plane to a reasonable distance
        base.camLens.setFar( 10000 )
	
    	
    	########################################
    	# Load the Rail
 	########################################

	# Load Rail Attacher Dummy
        # This is the object that will actually follow the spline curve
	self.railAttacher = render.attachNewNode('railAttacher')
	
	# Load Rail Spline
	self.rail = Mopath.Mopath()
	self.rail.loadFile('Models/city/SuperCityCurve')
	self.rail.fFaceForward = 1

        # Create a Motionpath Interval to make the railAttacher follow the Rail
	self.railInterval = MopathInterval(self.rail, self.railAttacher,
                                           name = 'railInterval')


    	########################################
    	# Hook up Player to Rail
 	########################################

	# Initialize Player Starting Location
	ClassGlobals.p1.player.reparentTo(railDummy)
	ClassGlobals.p1.player.setPos(railDummy.getPos())


	########################################
	# Create Distance Fog & Background Color
	########################################

	# Create a new Fog Object
	self.foggy = Fog("foggy")
	self.fogColor = Vec4(0, 0.1, 0.2, 1)
	self.foggy.setColor(self.fogColor)
	self.foggy.setExpDensity(0.0005)
	render.setFog(self.foggy)
	
	# Exclude these from Fog
	ClassGlobals.p1.crosshair.setFogOff()
        self.skybox.setFogOff()

	# Make Background Black for Splash Screen
	base.setBackgroundColor(0.05, 0.05, 0.3)


	########################################
	# Load Lighting Setups
	########################################

	# Create a new Lighting Object
	self.outdoorLights = LoadLighting.Lighting()
	
	# Call function to Level1 Lighting Setup
	self.outdoorLights.level1Outdoors()


    ########################################
    # Run the Level
    ########################################
    # Begins the Level
    def playLevel(self):

	# Start Music
	LoadMusic.l1stageMusicInt.loop()

	# Starts Updating Raildummy to RailAttacher
	taskMgr.add(self.updateRail, 'RailUpdater')

	# Begin the Intro of the Level
	self.startIntro()

	# Reset the Lifebar
	ClassGlobals.hud1.resetLifebar()

	# Make Sure Player is Not Invincible
	ClassGlobals.p1.isInvincible = 0


    # Ends the Level
    def endLevel(self):

	# End Camera Tracking of Player
	ClassGlobals.lazyCamera1.togglePlayerTracking()

	# End Music
	LoadMusic.l1stageMusicInt.finish()

	# Hide Crosshair
	ClassGlobals.p1.crosshair.hide()

	# End Rail Movement
	self.railInterval.finish()

	# Reset Rail to Starting Position
	self.railInterval.setT(0)

	# Remove Task to Create Enemy Sequences
	taskMgr.remove('enemySchedule')

	# Remove Move Player Task
        taskMgr.remove('moveP1')

	# End Updating of Raildummy to RailAttacher
	taskMgr.remove('RailUpdater')

	# Removes the Lazy Cam
	ClassGlobals.lazyCamera1.removeLazyCam()

	# Reset Form Counter Variable
	self.formCounter = 0

	# Stop Lazy-Camera System 
	ClassGlobals.lazyCamera1.removeLazyCam()


    # Begin Moving Along the Rail
    def startRail(self):

	# Start Moving on Rail
	self.railInterval.start()

	# Start FlyBy Sound Effect
   	SoundFX.IntroFlyBy.start()


    # Start Opening Cut-Scene
    def startIntro(self):

	# Initialize Player Position
	ClassGlobals.p1.player.setPos(0, 0, 20)
	ClassGlobals.p1.craft.setPos(0, 0, -1)

	# Start Camera Tracking of Player
	ClassGlobals.lazyCamera1.togglePlayerTracking()

	# Activate Movie Bars
 	ClassGlobals.hud1.toggleMovieBars()

	# For Timing of Camera Shot
	intro = Sequence(Func(self.cameraPosition1),
			Func(ClassGlobals.hud1.showLevelName),
			Wait(1.0),
			Func(self.startRail), 
			Wait(5.5),
			Func(self.cameraPosition2),
			Wait(2.0),
			Func(ClassGlobals.lazyCamera1.togglePlayerTracking),
			Func(self.finishCutScene),
			Func(ClassGlobals.hud1.toggleMovieBars))
	intro.start()


    # Function to Position Camera for Intro
    def cameraPosition1(self):
	# Positions Camera for Intro Cut-Scene
	camera.setPos(25, 3500, 55)

    # Function to Position Camera for Intro
    def cameraPosition2(self):
	# Pose the Main Character
	ClassGlobals.p1.playerFSM.request('coolPose')

	# Broadcast first message from Commander to Player
	ClassGlobals.hud1.chatBox1.startTransmission(0)

	# Positions Camera for Intro Cut-Scene
	camera.reparentTo(ClassGlobals.p1.player)
	camera.setPos(ClassGlobals.p1.craft, -25, -150, 0)

    # Starts Normal Camera Tracking
    def finishCutScene(self):
	# Camera and Player Movement Tasks need to be started near 
	# Simultaneously or will have Twitching Problems

	# Add Move Player Task
        taskMgr.add(ClassGlobals.p1.movePlayer, 'moveP1')

	# Start Lazy-Camera System 
	ClassGlobals.lazyCamera1.startLazyCam()

	# Start Enemy Sequence
	taskMgr.add(self.enemySchedule, 'enemySchedule')

	# Show the HUD
	ClassGlobals.hud1.showHUD()

	# Show Crosshair
	ClassGlobals.p1.crosshair.show()


    # Updates the RailDummy each frame to the position of the Rail Attacher
    def updateRail(self, task):
	railDummy.setPos(self.railAttacher.getPos())
	self.skybox.setPos(self.railAttacher.getPos())
	
	return Task.cont


    # Schedules when and where each Enemy Formation Appears during
    # the course of the Level
    def enemySchedule(self, task):

	if task.time >= 3.0 and self.formCounter == 0:
		self.formCounter += 1
		ClassGlobals.hud1.chatBox1.startTransmission(1)
	  	shots = -3 + ClassGlobals.difficulty
		EnemyFormations.enemyWall(7, 0, 'light', 6, shots, 2, 1.0)

	if task.time > 6.0 and self.formCounter == 1:
		self.formCounter += 1
	  	shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 5, 'standard', 5, shots, 2, shootDelay, 7)
        	EnemyFormations.enemyRightFlank(3, 5, 'standard', 5, shots, 2, shootDelay, 7)

	if task.time > 10.0 and self.formCounter == 2:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemySneakAttack(3, 15, 'standard', 12, shots, 2, shootDelay)
	
	if task.time > 12.0 and self.formCounter == 3:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemySneakAttack(3, 15, 'standard', 12, shots, 2, shootDelay)

	if task.time > 14.0 and self.formCounter == 4:
		self.formCounter += 1
		ClassGlobals.hud1.chatBox1.startTransmission(2)
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.0 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 10, 'heavy', 3, shots, 1, shootDelay, 6)
        	EnemyFormations.enemyRightFlank(3, 10, 'heavy', 3, shots, 1, shootDelay, 6)

	if task.time > 20.0 and self.formCounter == 5:
	    	self.formCounter += 1
		shots = 1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)

		EnemyFormations.miniBoss1(0, 30, 3, shots, shootDelay, 16)

		shots = 0 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 12, 'standard', 5, shots, 4, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 12, 'standard', 5, shots, 4, shootDelay, 10)


	if task.time > 23.0 and self.formCounter == 6:
	    	self.formCounter += 1
		ClassGlobals.hud1.chatBox1.startTransmission(4)


	if task.time > 36.0 and self.formCounter == 7:
	 	self.formCounter += 1
		shots = 0
		ClassGlobals.hud1.chatBox1.startTransmission(3)
		EnemyFormations.enemyWall(5, 0, 'light', 6, shots, 2, 1.0)
		EnemyFormations.enemyWall(5, 5, 'light', 6, shots, 2, 1.0)
	 	EnemyFormations.enemyWall(5, -5, 'light', 6, shots, 2, 1.0)

	if task.time > 37.0 and self.formCounter == 8:
	 	self.formCounter += 1
		shots = 0
		EnemyFormations.enemyWall(3, 2, 'light', 7, shots, 2, 1.0)
		EnemyFormations.enemyWall(3, 7, 'light', 7, shots, 2, 1.0)
	 	EnemyFormations.enemyWall(3, -3, 'light', 7, shots, 2, 1.0)

	if task.time > 38.0 and self.formCounter == 9:
	 	self.formCounter += 1
		shots = 0
		EnemyFormations.enemyWall(3, -1, 'light', 7, shots, 2, 1.0)
		EnemyFormations.enemyWall(3, 4, 'light', 7, shots, 2, 1.0)
	 	EnemyFormations.enemyWall(3, -6, 'light', 7, shots, 2, 1.0)


	if task.time > 41.0 and self.formCounter== 10:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyHoverAssault(3, 80, 30, 'standard', 3, shots, 2, shootDelay)

	if task.time > 42.0 and self.formCounter == 11:
		self.formCounter += 1		
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyHoverAssault(3, 100, 40, 'standard', 3, shots, 2, shootDelay)

	if task.time > 43.0 and self.formCounter == 12:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyHoverAssault(3, 120, 50, 'standard', 3, shots, 2, shootDelay)


	if task.time > 50.0 and self.formCounter== 13:
		self.formCounter += 1
		shots = 0 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyDropAssault(3, 80, 30, 'heavy', 3, shots, 2, shootDelay)

	if task.time > 51.0 and self.formCounter == 14:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyDropAssault(3, 100, 40, 'heavy', 3, shots, 2, shootDelay)

	if task.time > 52.0 and self.formCounter == 15:
		self.formCounter += 1
		ClassGlobals.hud1.chatBox1.startTransmission(6)
		shots = 0 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemyDropAssault(3, 120, 50, 'heavy', 3, shots, 2, shootDelay)


	if task.time > 60.0 and self.formCounter == 16:
		self.formCounter += 1
		shots = 4 + ClassGlobals.difficulty
		EnemyFormations.miniBoss1(0, 30, 3, shots, 3.0, 20)

		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
        	EnemyFormations.enemyLeftFlank(3, 10, 'heavy', 5, shots, 1, shootDelay, 10)
		EnemyFormations.enemyRightFlank(3, 10, 'heavy', 5, shots, 1, shootDelay, 10)

		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 25, 'standard', 6, 1, shots, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 25, 'standard', 6, 1, shots, shootDelay, 10)

	if task.time > 74.0 and self.formCounter == 17:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemySneakAttack(3, 12, 'standard', 12, shots, 2, shootDelay)
	
	if task.time > 76.0 and self.formCounter == 18:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.enemySneakAttack(3, 12, 'standard', 12, shots, 2, shootDelay)

		shots = -2 + ClassGlobals.difficulty
		shootDelay = 4.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 10, 'standard', 3, shots, 3, shootDelay, 6)
        	EnemyFormations.enemyRightFlank(3, 10, 'standard', 3, shots, 3, shootDelay, 6)	 
	        EnemyFormations.enemyLeftFlank(3, 20, 'standard', 5, shots, 3, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 20, 'standard', 5, shots, 3, shootDelay, 10)

	if task.time > 84.0 and self.formCounter == 19:
		self.formCounter += 1
		shots = 2 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
		EnemyFormations.miniBoss1(13, 30, 3, shots, shootDelay, 20)
		EnemyFormations.miniBoss1(-13, 30, 3, shots, shootDelay, 20)

	if task.time > 100.0 and self.formCounter == 20:
	    	self.formCounter += 1
		ClassGlobals.hud1.chatBox1.startTransmission(7)
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)
        	EnemyFormations.enemyRightFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)	 
	        EnemyFormations.enemyLeftFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)

	# Fight the Boss!!!
	if task.time > 110.0 and self.formCounter == 21:
	    	self.formCounter += 1
	   	EnemyFormations.bossBattle()

	if task.time > 115.0 and self.formCounter == 22:
	    	self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty		
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)
        	EnemyFormations.enemyRightFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)	 
	        EnemyFormations.enemyLeftFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)

	if task.time > 125.0 and self.formCounter == 23:
		self.formCounter += 1
		shots = 0 + ClassGlobals.difficulty
		shootDelay = 3.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 15, 'heavy', 3, shots, 1, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 15, 'heavy', 3, shots, 1, shootDelay, 10)

	if task.time > 140.0 and self.formCounter == 24:
		self.formCounter += 1
		shots = -1 + ClassGlobals.difficulty
		shootDelay = 2.5 - (ClassGlobals.difficulty*0.5)
	        EnemyFormations.enemyLeftFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)
        	EnemyFormations.enemyRightFlank(3, 10, 'standard', 3, shots, 1, shootDelay, 6)	 
	        EnemyFormations.enemyLeftFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)
        	EnemyFormations.enemyRightFlank(3, 20, 'standard', 5, shots, 4, shootDelay, 10)

	return Task.cont

