# common scoring 

import host
import bf2
import math

from bf2.stats.constants import *
from bf2 import g_debug

SCORE_KILL = 0
SCORE_TEAMKILL = 1
SCORE_SUICIDE = 0
SCORE_REVIVE = 0
SCORE_HEAL = 0
SCORE_RESUPPLY = 0
SCORE_REPAIR = 0
SCORE_STRATEGIC = 0
SCORE_STRATEGICREPAIR = 0
SCORE_EMP = 0
SCORE_DAMAGETITAN = 0
SCORE_DESTROYTITANWEAPON = 0
SCORE_REPAIRTITANWEAPON = 0
SCORE_DESTROYTITANPART = 0
SCORE_REPAIRTITANPART = 0
SCORE_DESTROYTITANCORE = 0
SCORE_REPAIRTITANCORE = 0
SCORE_DESTROYGROUNDCANNON = 0
SCORE_REPAIRGROUNDCANNON = 0
SCORE_DESTROYMISSILE = 0
SCORE_TEAMDAMAGE = 0
SCORE_TEAMVEHICLEDAMAGE = 0
SCORE_DESTROYREMOTECONTROLLED = 0
SCORE_DESTROYVEHICLESHIELD = 0
SCORE_DESTROYIFVVEHICLE = 0
SCORE_DROP = 0

SCORE_TITANATTACKBONUS = 0
SCORE_TITANDEFENDBONUS = 0
SCORE_MAX_SQUADORDER = 1 # to make this tweakable
SCORE_SO_REVIVE = 0
SCORE_SO_HEAL = 0
SCORE_SO_REPAIR = 0
SCORE_SO_REAMMO = 0
SCORE_SO_KILL = 0
SCORE_SO_PICKUP = 0
SCORE_SO_DROP = 0

SCORE_SM_REVIVE = 0
SCORE_SM_HEAL = 0
SCORE_SM_REPAIR = 0
SCORE_SM_REAMMO = 0
SCORE_SM_DROP = 0

SCORE_SM_KILLASSIST_DRIVER = 0
SCORE_SM_KILLASSIST_PASSENGER = 0
SCORE_SM_KILLASSIST_TARGETER = 0
SCORE_SM_KILLASSIST_DAMAGE = 0

SCORE_KILLASSIST_DRIVER = 0
SCORE_KILLASSIST_PASSENGER = 0
SCORE_KILLASSIST_TARGETER = 0
SCORE_KILLASSIST_DAMAGE = 0

TITAN_BONUSSCOREDIST = 100
TITAN_HORISONTAL_DROPDIST = 100
TITAN_VERTICAL_DROPDIST = 200
TITAN_HORISONTAL_SPAWNDIST = 3000
TITAN_VERTICAL_SPAWNDIST = 1500
TITAN_ENTER_DISTANCE = 300

REPAIR_POINT_LIMIT = 1
HEAL_POINT_LIMIT = 1
GIVEAMMO_POINT_LIMIT = 1
TEAMDAMAGE_POINT_LIMIT = 1
TEAMVEHICLEDAMAGE_POINT_LIMIT = 1

REPLENISH_POINT_MIN_INTERVAL = 30	# seconds
SQUADLEADER_BONUS_MOD = 1
COMMANDER_BONUS_MOD = 1
AWAY_BONUS_MOD = 2

TITAN_WEAPONS_DESTROY_LIMIT = 1
TITAN_WEAPONS_REPAIR_LIMIT = 1
TITAN_WEAPONS_TIME_LIMIT = 10

MAX_EMP_TIME = 5

# sub score
NORMAL = 0
SKILL = 1
RPL = 2
CMND = 3
SQUAD = 4
SQUADMEMBER = 5

g_titansMap = [] # cache, as this map won't change

def init():

	# set limits for how many repair HPs etc are needed to get a callback

	bf2.gameLogic.setTeamVehicleDamagePointLimit(TEAMVEHICLEDAMAGE_POINT_LIMIT)
	
	host.registerGameStatusHandler(onGameStatusChanged)
	
	if g_debug: print "scoring common init"



def onGameStatusChanged(status):
	global g_titansMap

	if status == bf2.GameStatus.Playing:
		g_titansMap = []
			
		host.registerHandler('PlayerKilled', onPlayerKilled)
		host.registerHandler('PlayerDeath', onPlayerDeath)
		host.registerHandler('PlayerTeamDamagePoint', onPlayerTeamDamagePoint)
		host.registerHandler('PlayerScore', onPlayerScore)
		host.registerHandler('VehicleDestroyed', onVehicleDestroyed)
		host.registerHandler('ExitVehicle', onExitVehicle)
		host.registerHandler('EnterVehicle', onEnterVehicle)
		
	
	elif status == bf2.GameStatus.EndGame:

		giveCommanderEndScore(bf2.playerManager.getCommander(1), bf2.gameLogic.getWinner())
		giveCommanderEndScore(bf2.playerManager.getCommander(2), bf2.gameLogic.getWinner())
		
			
# give commander score for every player score
def addScore(player, points, subScore = NORMAL, subPoints = -1):

	# commander doesnt get score for regular actions, only for pure commander tasks. he also gets punishing points.
	if not player.isCommander() or subScore == CMND or points < 0:
	
		if subPoints == -1:
			subPoints = points

		# sub score
		if subScore == RPL:
			player.score.rplPyScore += subPoints			
			player.score.rplScore = int(player.score.rplPyScore)
		elif subScore == SKILL:
			player.score.skillScore += subPoints
		elif subScore == CMND:
			player.score.cmdPyScore += subPoints
			if bf2.serverSettings.getGameMode() == "gpm_ti":
					player.score.cmdTitanScore += subPoints
		elif subScore == SQUAD:			
			#Give the squadleader of the player a bonus score for every squad point
			giveSquadMemberBonusScore(player, points)
			giveSquadLeaderBonusScore(player, points)				
		elif subScore == SQUADMEMBER:	
			giveSquadMemberBonusScore(player, points)
		
		giveAwayBonysScore(player, points)	
		
		if subScore != SQUAD and subScore != SQUADMEMBER:
			player.score.score += points

	# commander score
	commander = bf2.playerManager.getCommander(player.getTeam())
	if commander != None and commander.isValid() and subScore != CMND and player != commander and points > 0:
		numPlayers = bf2.playerManager.getNumberOfAlivePlayersInTeam(commander.getTeam())
		if numPlayers > 0:
			scoreGotten = float(points) / numPlayers
			commander.score.score += scoreGotten 
			if scoreGotten > 0:
				commander.score.cmdPyScore += scoreGotten
				if bf2.serverSettings.getGameMode() == "gpm_ti":
					commander.score.cmdTitanScore += scoreGotten

def giveAwayBonysScore(player, points):
	if player == None: return
	
	#Add away bonus points if the played time is less then the remaining away bonus time
	if player.stats.timePlayed < player.stats.awayBonusTimeLeft:
		player.score.awayBonusScore += float(points)/float(AWAY_BONUS_MOD)
		if g_debug: print player.getName(), " has ", int(player.score.awayBonusScore), " away bonus score"

def giveCommanderEndScore(player, winningTeam):
	if player == None: return

	multiplier = 1

	if player.getTeam() == winningTeam: 
		multiplier = 2
	
	# double the commander score and add to regular score
	player.score.score = (player.score.score + player.score.fracScore - player.score.cmdPyScore) + player.score.cmdPyScore * multiplier
	player.score.cmdScore = int(player.score.cmdPyScore * multiplier)

	if bf2.serverSettings.getGameMode() == "gpm_ti":
		player.score.cmdTitanScore = player.score.cmdPyScore
	
def giveSquadMemberBonusScore(player, points):
	if player and points != 0 and player.getSquadId() > 0 and not player.isSquadLeader():
		player.score.experienceScore += points
		player.score.squadMemberBonusScore += points
		if g_debug: print player.getName(), " has ", player.score.squadMemberBonusScore, " SM score"

def giveSquadLeaderBonusScore(player, points):
	if player and points != 0 and player.getSquadId() > 0 and not player.isSquadLeader():
		squadLeader = bf2.playerManager.getSquadLeader(player.getTeam(), player.getSquadId())
		
		if squadLeader != None:
			oldScore = squadLeader.score.squadLeaderBonusScore
			squadLeader.score.experienceScore += float(points)/float(SQUADLEADER_BONUS_MOD)
			squadLeader.score.squadLeaderBonusScore += float(points)/float(SQUADLEADER_BONUS_MOD)
			giveCommanderBonusScore(player, points)

			#Only send when player actually will recieve a score in EOR
			if int(squadLeader.score.squadLeaderBonusScore) > int(oldScore):
				bf2.gameLogic.sendGameEvent(squadLeader, 21, 1) #21 = Bonus, 1 = SquadLeader

			if g_debug: print squadLeader.getName(), " has ", squadLeader.score.squadLeaderBonusScore, " SL score"
			
			

def giveCommanderBonusScore(player, points):
	if player and points != 0:
		commander = bf2.playerManager.getCommander(player.getTeam())
		if commander != None and commander.isValid() and player != commander:
			oldScore = commander.score.commanderBonusScore			
			commander.score.experienceScore += float(points)/float(COMMANDER_BONUS_MOD)
			commander.score.commanderBonusScore += float(points)/float(COMMANDER_BONUS_MOD)

			#Only send when player actually will recieve a score in EOR
			if int(commander.score.commanderBonusScore) > int(oldScore):
				bf2.gameLogic.sendGameEvent(commander, 21, 2) #21 = Bonus, 2 = Commander

			if g_debug: print commander.getName(), " has ", commander.score.commanderBonusScore, " commander score"

def onPlayerKilled(victim, attacker, weapon, assists, object):

	killedByEmptyVehicle = False
	countAssists = False
	
	# killed by unknown, no score
	if attacker == None:
		
		# check if killed by vehicle in motion
		if weapon == None and object != None:
			if hasattr(object, 'lastDrivingPlayerIndex'):
				attacker = bf2.playerManager.getPlayerByIndex(object.lastDrivingPlayerIndex)
				killedByEmptyVehicle = True
			elif hasattr(object, 'lastEMPedTime') and object.lastEMPedTime + MAX_EMP_TIME > host.timer_getWallTime():
				attacker = object.lastEMPedBy

		if attacker == None:
			#Could have been jumping off a cliff and someone could have done him some damage before...
			countAssists = True				
			if g_debug: print "No attacker found"

	#bail out if we have no victim (happens when killing auto controllers)
	if victim == None:
		return
	
	victimVehicle = victim.getVehicle()


	# killed by remote controlled vehicle, no score awarded in this game
	if victimVehicle and victimVehicle.isPlayerControlObject and victimVehicle.getIsRemoteControlled():
		pass
		
	# no attacker, killed by object
	elif attacker == None:
		#Could have been jumping off a cliff and someone could have done him some damage before...
		countAssists = True
		
	# killed by self
	elif attacker == victim:

		# no suicides from own wreck
		if killedByEmptyVehicle and object.getIsWreck():
			return

		attacker.score.suicides += 1
		addScore(attacker, SCORE_SUICIDE, RPL)
			
	# killed by own team	
	elif attacker.getTeam() == victim.getTeam():

		# no teamkills from wrecks
		if object != None and object.getIsWreck():
			return
			
		# no teamkills from artillery
		if weapon:
			attackerVehicle = bf2.objectManager.getRootParent(weapon)
			if attackerVehicle and attackerVehicle.isPlayerControlObject and attackerVehicle.getIsRemoteControlled():
				return

		attacker.score.kills += 1
		addScore(attacker, SCORE_KILL, RPL)
				
		countAssists = True


	# killed by enemy
	else:
		pass
			

def onExitVehicle(player, vehicle):
	rootVehicle = bf2.objectManager.getRootParent(vehicle)

	if rootVehicle.isPlayerControlObject:
		playersInVehicle = rootVehicle.getOccupyingPlayers()
	
		for p in playersInVehicle:
			if p == playersInVehicle[0]:
				giveTitanDropPoint(p, player, vehicle)
				return

def onEnterVehicle(player, vehicle, freeSoldier = False):
	if player == None:
		return

	closestTitan = getClosestTitan(vehicle, TITAN_ENTER_DISTANCE, player.getTeam())

	if closestTitan and player:
		player.stats.enteredCloseToTitan = True
		return
	
	player.stats.enteredCloseToTitan = False
	
def onPlayerDeath(victim, vehicle):
	if victim == None:
		return

	victim.score.deaths += 1
	


# prevent point-exploiting by replenishing same player again
def checkGrindBlock(player, object):

	if object.isPlayerControlObject:
		defPlayers = object.getOccupyingPlayers()
		if len(defPlayers) > 0:
			defPlayer = defPlayers[0]
			
			if not hasattr(player, 'lastReplenishPointMap'):
				player.lastReplenishPointMap = {}
			else:	
				if defPlayer.index in player.lastReplenishPointMap:
					if player.lastReplenishPointMap[defPlayer.index] + REPLENISH_POINT_MIN_INTERVAL > host.timer_getWallTime():
						return True
					
			player.lastReplenishPointMap[defPlayer.index] = host.timer_getWallTime()

	return False
	

def giveDriverSpecialPoint(player):

	# special point given to driver, if someone in vehicle gets an abilitypoint
	vehicle = player.getVehicle()
	if vehicle and vehicle.isPlayerControlObject:
		rootVehicle = bf2.objectManager.getRootParent(vehicle)
		occPlayers = rootVehicle.getOccupyingPlayers()

		if len(occPlayers) > 0:
			driver = occPlayers[0]
	
			if driver != None and driver != player and driver.getVehicle() == rootVehicle:
				driver.score.driverSpecials += 1
				addScore(driver, 1, RPL)
				bf2.gameLogic.sendGameEvent(driver, 10, 3) #10 = Replenish, 3 = DriverAbility	
	
def onPlayerTeamDamagePoint(player, object):
	vehicleType = getVehicleType(object.templateName)
	
	if player.isCommander():
		return
	
	if vehicleType == VEHICLE_TYPE_SOLDIER:
		if player.isAlive() and not player.isManDown(): #Don't give point if victim died - teamkill handles this
			player.score.teamDamages += 1
			addScore(player, SCORE_TEAMDAMAGE, RPL)
	else:
		player.score.teamVehicleDamages += 1
		addScore(player, SCORE_TEAMVEHICLEDAMAGE, RPL)

def onPlayerScore(player, score):
	if g_debug: print player.getName(), "recieved ", score, " points"


def onVehicleDestroyed(vehicle, attacker):
	if attacker != None and checkIfOtherTeam(vehicle, attacker):
		if vehicle.getIsRemoteControlled() and not checkTitan(vehicle):
			addScore(attacker, SCORE_DESTROYREMOTECONTROLLED, RPL)
			bf2.gameLogic.sendGameEvent(attacker, 10, 5) #10 = Replenish, 5 = DestroyStrategic
		elif string.lower(vehicle.templateName) == "eu_ifv" or string.lower(vehicle.templateName) == "as_ifv":
			addScore(attacker, SCORE_DESTROYIFVVEHICLE, RPL)
			bf2.gameLogic.sendGameEvent(attacker, 10, 5) #10 = Replenish, 5 = DestroyStrategic


def checkIfSameTeam(object, player):
	root = bf2.objectManager.getRootParent(object)

	if root.isValid() and player:
		if root.getTeam() == player.getTeam():
			return 1
	return 0

def checkIfOtherTeam(object, player):
	root = bf2.objectManager.getRootParent(object)

	if root.isValid() and player:
		if root.isPlayerControlObject:
			if root.getTeam() != player.getTeam() and root.getTeam() != 0:
				return 1
	return 0

# if accepted an order from squad leader/commander and is inside radius of order marker, 
# you get extra point for killing enemy/healing/repairing/give ammo to team mate
def giveExtraOrderPoint(player, object, points, onlySquadMember = 0):
	if player and object and player.getIfAcceptedAttackDefendOrder() and points > 0 and object.isPlayerControlObject:
		isSquadLeader = onlySquadMember
		givePoint = False

		playersInVehicle = object.getOccupyingPlayers()

		for reciever in playersInVehicle:
			if reciever and reciever.getSquadId() == player.getSquadId() and reciever.getTeam() == player.getTeam():
				givePoint = True
				if not reciever.isSquadLeader():
					isSquadLeader = False
					
		if not givePoint and onlySquadMember:
			return

		distanceTo = getVectorDistance(player.getOrderPosition(), object.getPosition())
	
		if distanceTo < player.getOrderRadius():
			if not player.isSquadLeader():
				oldScore = player.score.squadMemberBonusScore

				if isSquadLeader:
					addScore(player, points, SQUADMEMBER)
				else:
					addScore(player, points, SQUAD)
				
				#Only send when player actually will recieve a score in EOR
				if int(player.score.squadMemberBonusScore) > int(oldScore):
					bf2.gameLogic.sendGameEvent(player, 21, 3) #21 = Bonus, 3 = SquadOrder

			player.setMaxOrderPoint(SCORE_MAX_SQUADORDER)
			player.setOrderPoint(points)

# if the subject is a member of the attackers squad you get extra point for healing/repairing/give ammo to squad mate
def giveExtraSquadPoint(giver, object, points):
		if object and giver and giver.getSquadId() > 0 and object.isPlayerControlObject:
			playersInVehicle = object.getOccupyingPlayers()
			for reciever in playersInVehicle:
				if reciever and points > 0 and reciever.getSquadId() == giver.getSquadId() and reciever.getTeam() == giver.getTeam():
					if not giver.isSquadLeader():
						oldScore = giver.score.squadMemberBonusScore

						if not reciever.isSquadLeader():
							addScore(giver, points, SQUAD)
						else:
							addScore(giver, points, SQUADMEMBER)
		
						#Only send when player actually will recieve a score in EOR
						if int(giver.score.squadMemberBonusScore) > int(oldScore):
							bf2.gameLogic.sendGameEvent(giver, 21, 0) #21 = Bonus, 3 = SquadMember

					giver.setMaxOrderPoint(SCORE_MAX_SQUADORDER)
					giver.setOrderPoint(points)
					return

# if the subject is a member of the attackers squad you get extra point for healing/repairing/give ammo to squad mate
def giveExtraSquadAssistPoint(attacker, assister, points):
	if assister and attacker and points > 0 and assister.getSquadId() > 0 and assister.getSquadId() == attacker.getSquadId() and assister.getTeam() == attacker.getTeam():
		if not assister.isSquadLeader():
			oldScore = assister.score.squadMemberBonusScore

			if not attacker.isSquadLeader():
				addScore(assister, points, SQUAD)
			else:
				addScore(assister, points, SQUADMEMBER)
			
			#Only send when player actually will recieve a score in EOR
			if int(assister.score.squadMemberBonusScore) > int(oldScore):
				bf2.gameLogic.sendGameEvent(assister, 21, 4) #21 = Bonus, 3 = SquadAssist
			
		assister.setMaxOrderPoint(SCORE_MAX_SQUADORDER)
		assister.setOrderPoint(points)	

# if the enemy is close to titan when killed you get extra point for killing him
def giveExtraTitanPoint(player, object):
	if player == None:
		return

	#Titan is now closer to gound and this needs to be restricted
	#closestTitan = getClosestTitan(object, TITAN_BONUSSCOREDIST)

	closestTitan = getTitanBelow(player, TITAN_BONUSSCOREDIST, TITAN_BONUSSCOREDIST, -1, 20)
	
	if closestTitan:
			if closestTitan.getTeam() == player.getTeam():
				if g_debug: print player.getName(), " defended his titan"
				player.score.titanDefendKills += 1

				if SCORE_TITANDEFENDBONUS > 0:
					bf2.gameLogic.sendGameEvent(player, 21, 5) #21 = Bonus, 5 = TitanDefend

				addScore(player, SCORE_TITANDEFENDBONUS, RPL)				
			else:
				if g_debug: print player.getName(), " attacked enemy titan"
				player.score.titanAttackKills += 1

				if SCORE_TITANATTACKBONUS > 0:
					bf2.gameLogic.sendGameEvent(player, 21, 6) #21 = Bonus, 6 = TitanAttack

				addScore(player, SCORE_TITANATTACKBONUS, RPL)

# if the enemy is close to titan when killed you get extra point for killing him
def giveTitanDropPoint(driver, player, object):
	if driver == None or player == None:
		return
	
	closestTitan = getTitanBelow(player, TITAN_HORISONTAL_DROPDIST, TITAN_VERTICAL_DROPDIST, player.getTeam())
	
	if closestTitan:
			if driver != player:
				if not player.stats.enteredCloseToTitan:
					if g_debug: print player.getName(), " dropped on enemy titan"
					addScore(driver, SCORE_DROP, RPL)
					driver.score.titanAirDrops += 1
					bf2.gameLogic.sendGameEvent(driver, 10, 3) #10 = Replenish, 3 = DriverAbility
				
					playerVehicle = player.getVehicle()

					if playerVehicle.isValid():
						giveExtraSquadPoint(driver, playerVehicle, SCORE_SM_DROP)
						giveExtraOrderPoint(driver, playerVehicle, SCORE_SO_DROP, True)				

#Get the titan below the player
def getTitanBelow(player, horisontalMaxDist, verticalMaxDist, team=-1, verticalOffset=0):
	if player == None:
		return 0

	vehicle = player.getVehicle()

	if vehicle.isValid():
		for titan in g_titansMap:
			titanPos = titan.getPosition()
			vehiclePos = vehicle.getPosition()
			horisontalDist = getVectorDistance([titanPos[0], 0.0, titanPos[2]], [vehiclePos[0], 0, vehiclePos[2]])
			verticalDist = getVectorDistance([0.0, titanPos[1], 0.0], [0.0, vehiclePos[1], 0.0])

			if horisontalDist <= horisontalMaxDist and verticalDist <= verticalMaxDist and (titanPos[1] - verticalOffset) < vehiclePos[1]:
				if titan.getTeam() != team:
					if g_debug: print player.getName(), " ", horisontalDist, "m, ", verticalDist, "m, ", "from titan"
					return titan		
	return 0	


def getClosestTitan(object, distance, team=-1):
	if object.isValid():
		for titan in g_titansMap:
			distanceTo = getVectorDistance(titan.getPosition(), object.getPosition())
			if distanceTo < distance:
				if titan.getTeam() != team:
					if g_debug: print distanceTo, "m from titan" 
					return titan
	
	return 0


# get distance between two positions
def getVectorDistance(pos1, pos2):
	diffVec = [0.0, 0.0, 0.0]
	diffVec[0] = math.fabs(pos1[0] - pos2[0])
	diffVec[1] = math.fabs(pos1[1] - pos2[1])
	diffVec[2] = math.fabs(pos1[2] - pos2[2])
	 
	return math.sqrt(diffVec[0] * diffVec[0] + diffVec[1] * diffVec[1] + diffVec[2] * diffVec[2])		


def date():
	return host.timer_getWallTime()
