## Copyright ClassicThunder 2006-2007
## CreateNests by Sto
## Everything else by ClassicThunder

from CvPythonExtensions import *
import CvUtil
import CvScreensInterface
import CvDebugTools
import CvWBPopups
import PyHelpers
import Popup as PyPopup
import CvCameraControls
import CvTopCivs
import sys
import CvWorldBuilderScreen
import CvAdvisorUtils
import CvTechChooser
import pickle
import math
import string
from CvPythonExtensions import *
import CvMapGeneratorUtil

gc = CyGlobalContext()
localText = CyTranslator()
PyPlayer = PyHelpers.PyPlayer
PyInfo = PyHelpers.PyInfo
					
class DragonTools:
	def __init__(self):
		self.DragonLairs = []
		self.RampageTurn = 0
		self.Dragon = 0
		self.AttackingDragons = 0

	def CreateNests(self):
			# initializing datas :
			map = CyMap()
			dice = gc.getGame().getSorenRand()
			iW = map.getGridWidth()
			iH = map.getGridHeight()
			iBonusDragon = gc.getInfoTypeForString('BONUS_DRAGONROOST')
			iUnitDragon = gc.getInfoTypeForString('UNIT_LAIRDRAGON')
			bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())

			# the minimum distance between a dragon and an unit on the same area (by air)
			iRangeUnit = 4

			# the minimum distance between two dragons on the same area (by air , like for units)
			iRangeDragons = 8

			# the number of time that the function try to place a nest near player per area .
			# As the plot is aleatory selected and if the size of the area is at the limit ,
			# sometimes the first plot can prevent the second to be placed ( for an example )
			iPass = 10

			#create library to stock plot coordinates per areas into two libraries : with starting locations and without
			areaPlotsPlayers = {}
			areaPlotsIslands = {}

			areas = CvMapGeneratorUtil.getAreas()
			for area in areas :
					if area.isWater() : continue
					# else create a list where to store plot coordinates into the libraries
					if area.getNumStartingPlots()>0 : areaPlotsPlayers[area.getID()]=[]
					else : areaPlotsIslands[area.getID()]=[]

			# create the list of team . used to check visibility .
			teamList=[]
			for iTeam in range(gc.getMAX_TEAMS()):
					iLoopTeam = gc.getTeam(iTeam)
					if iLoopTeam.isNone() : continue
					if iLoopTeam.isBarbarian() : continue
					if not iLoopTeam.isAlive() : continue
					teamList.append(iTeam)

			# create a mask of coordinates plot <= iRangeUnit squares around every non barbarian units
			maskPlots = []
			for iX in range(iW) :
					for iY in range(iH) :
							pLoopPlot = map.plot(iX, iY)
							if pLoopPlot.getNumUnits()==0 : continue

							# if the unit is barbarian , just remove the barbarian plot from the possible nest placement
							if pLoopPlot.getUnit(0).isBarbarian() :
									maskPlots.append((iX, iY))
									continue

							# else add iRangeUnit square around to mask
							for dX in range(-iRangeUnit,iRangeUnit+1):
									for dY in range(-iRangeUnit,iRangeUnit+1):
											pX = iX + dX
											if map.isWrapX() : pX = pX % iW # if the map is wrapX . make the coordinate ( 0 <= pX < iW )
											pY = iY + dY
											if map.isWrapY() : pY = pY % iH # if the map is wrapY . make the coordinate ( 0 <= pY < iH )
											maskPlots.append((pX, pY))
	        
			# create the list of plots per area
			for iX in range(iW) :
					for iY in range(iH) :
							pLoopPlot = map.plot(iX, iY)
	                        
							if pLoopPlot.isWater() : continue
							if pLoopPlot.isImpassable() : continue
							if pLoopPlot.isOwned() : continue # made for scenarios/scripts with barbarian cities at the beginning of the game
	                        
							bVisible=False
							for iTeam in teamList :
									if pLoopPlot.isVisible(iTeam ,False) : bVisible=True
							if bVisible : continue

							if (iX,iY) in maskPlots : continue

							iArea = pLoopPlot.getArea()

							if iArea in areaPlotsPlayers.keys() : areaPlotsPlayers[iArea].append((iX,iY))
							elif iArea in areaPlotsIslands.keys() : areaPlotsIslands[iArea].append((iX,iY))
							
			# first try to place every nest on players areas .
			bNearPlayer = True
			listNestPlots = []
			for iArea in areaPlotsPlayers.keys() :
					nbNestToPlace = map.getArea(iArea).getNumStartingPlots()
					listAreaPlots = areaPlotsPlayers[iArea]
					for iTest in range(iPass):
							listNestPlotsTemp = []
							listAreaPlotsTemp = [item for item in listAreaPlots] # duplicate the list . now i can modify it without modifing the original
							while True :
									if len(listAreaPlotsTemp)==0 : break
									if len(listNestPlotsTemp)==nbNestToPlace : break
									iChoice = dice.get( len(listAreaPlotsTemp) , " Nest Plot coordinates ( players ). " )
									(iX,iY) = listAreaPlotsTemp[iChoice]
									listNestPlotsTemp.append((iX,iY))

									# create a mask of coordinates where the nest can t be placed
									maskPlotsTemp=[]
									for dX in range(-iRangeDragons,iRangeDragons+1):
											for dY in range(-iRangeDragons,iRangeDragons+1):
													pX = iX + dX
													if map.isWrapX() : pX = pX % iW # if the map is wrapX . make the coordinate ( 0 <= pX < iW )
													pY = iY + dY
													if map.isWrapY() : pY = pY % iH # if the map is wrapY . make the coordinate ( 0 <= pY < iH )
													maskPlotsTemp.append((pX, pY))

									# remove the mask plots from the possible nest plot list
									listAreaPlotsTemp = [ item for item in listAreaPlotsTemp if not item in maskPlotsTemp ]

							if len(listNestPlotsTemp)==nbNestToPlace : break # all nest placed in this area .

					if len(listNestPlotsTemp)!=nbNestToPlace :
							bNearPlayer = False
							break # nests can t be placed in this area . nests will be placed on islands

					listNestPlots += listNestPlotsTemp

			if not bNearPlayer : # place nests on island
					listNestPlots = []
					nbNestToPlace = gc.getGame().countCivPlayersAlive()
					listArea = [ areaPlotsIslands[iArea] for iArea in areaPlotsIslands.keys() ] # change the library into a list
	                
					# calculate the max number of nests per Areas
					lenlistArea = [ len(item) for item in listArea ]
					nbTilesTot = float(sum(lenlistArea ))
					nbMaxNests = [ int(lenlistArea[i]*nbNestToPlace/nbTilesTot)+1 for i in range(len(lenlistArea)) ]
	                
					# place nests
					nbNestsPlaced = 0
					while True :
							if nbNestsPlaced == nbNestToPlace : break
							if len(listArea)==0 : break
	                        
							iList = nbMaxNests.index(max(nbMaxNests)) # place first on biggest islands
							listAreaPlotsTemp = [ item for item in listArea[iList] ]
							nbPlacedArea = 0
							while True :
									if nbNestsPlaced == nbNestToPlace : break
									if nbPlacedArea == nbMaxNests[iList] : break
									if len(listAreaPlotsTemp)==0 : break

									iChoice = dice.get( len(listAreaPlotsTemp) , " Nest Plot coordinates ( islands ) . " )
									(iX,iY) = listAreaPlotsTemp[iChoice]
									listNestPlots.append((iX,iY))
									nbPlacedArea += 1
									nbNestsPlaced += 1

									# create a mask of coordinates where the nest can t be placed
									maskPlotsTemp=[]
									for dX in range(-iRangeDragons,iRangeDragons+1):
											for dY in range(-iRangeDragons,iRangeDragons+1):
													pX = iX + dX
													if map.isWrapX() : pX = pX % iW # if the map is wrapX . make the coordinate ( 0 <= pX < iW )
													pY = iY + dY
													if map.isWrapY() : pY = pY % iH # if the map is wrapY . make the coordinate ( 0 <= pY < iH )
													maskPlotsTemp.append((pX, pY))

									# remove the mask plots from the possible nest plot list
									listAreaPlotsTemp = [ item for item in listAreaPlotsTemp if not item in maskPlotsTemp ]

							del(listArea[iList])
							del(nbMaxNests[iList])

			# A message to check where nests were spawn .
			#if bNearPlayer : CyInterface().addImmediateMessage( "Nests spawn on players areas : "+str(len(listNestPlots))+" nests spawned." , "")
			#else : CyInterface().addImmediateMessage( "Nests spawn on islands : "+str(len(listNestPlots))+" nests spawned / "+str(nbNestToPlace)+" nests to place." , "")

			print repr(listNestPlots) # debug tool

			#Now create nests :
			for (iX,iY) in listNestPlots :
					pPlot = map.plot(iX, iY)
					if pPlot.isNone():
							print " pb plot : "+repr((iX,iY)) # debug tool
							continue
					pPlot.setBonusType( -1) # better to clear the plot bonus before adding another
					pPlot.setBonusType( iBonusDragon)
					bPlayer.initUnit( iUnitDragon, iX, iY, UnitAITypes.NO_UNITAI )
					self.DragonLairs.append( map.plotNum(iX,iY) )
					
			if (len(self.DragonLairs) == 0):
				CyInterface().addImmediateMessage("Error: No Dragons Spawned" , "")
				
	def DragonDoTurn(self):
		if (len(self.DragonLairs) != 0):
			if (CyGame().getGameTurn() == 0):
				self.RampageTurn = (40 + gc.getGame().getSorenRandNum( 11 ,"First Dragon Attack") )	
			elif (CyGame().getGameTurn() == self.RampageTurn):
				self.RampageTurn = int(( CyGame().getGameTurn() + 30 + gc.getGame().getSorenRandNum(11, "Dragon Attacks")	))
				if ((self.AttackingDragons + 1) <= len(self.DragonLairs) ):
					index = self.DragonLairs[self.Dragon]
					plot = CyMap().plotByIndex(index)
					unit = gc.getPlayer( gc.getBARBARIAN_PLAYER() ).initUnit( gc.getInfoTypeForString('UNIT_ATTACKDRAGON'), plot.getX(), plot.getY(), UnitAITypes.NO_UNITAI)
					unit.setUnitAIType(5)
					self.AttackingDragons = self.AttackingDragons + 1
					if ( (self.Dragon + 1) < (len(self.DragonLairs)) ):
						self.Dragon = self.Dragon + 1
						CyInterface().addImmediateMessage("Dragon Spawned" , "")
					else:
						self.Dragon = 0
						CyInterface().addImmediateMessage("Dragons Reset" , "")
	
	def NewNest(self, pUnit):
		if (len(self.DragonLairs) != 0):
			if (pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_ATTACKDRAGON')):
				pPlot = pUnit.plot()
				if ( (pPlot.getImprovementType() == gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS')) or (pPlot.isCity() == True) ):
					pPlot.setBonusType( gc.getInfoTypeForString('BONUS_DRAGONROOST') )
					pUnit.kill(True ,0)
					self.AttackingDragons = self.AttackingDragons - 1
					gc.getPlayer( gc.getBARBARIAN_PLAYER() ).initUnit( gc.getInfoTypeForString('UNIT_LAIRDRAGON'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
					self.DragonLairs.append( CyMap().plotNum(pPlot.getX(),pPlot.getY()) )
	
	def SaveData(self):
		CSString = (str(self.Dragon) + "|" + str(self.RampageTurn) + "|" + str(self.AttackingDragons))
		gc.getGame().setScriptData( pickle.dumps( CSString ))
		
	def GetData(self):
		Data = pickle.loads(gc.getGame().getScriptData())
		Numbers = string.split(Data,"|")
		self.Dragon = int(Numbers[0])
		self.RampageTurn = int(Numbers[1])
		self.AttackingDragons = int(Numbers[2])
		self.DragonLairs = []
		if (CyGame().getGameTurn() != 0):
			for index in range (CyMap().numPlots()):
				pPlot = CyMap().plotByIndex(index)
				if (pPlot.getBonusType(-1) == gc.getInfoTypeForString('BONUS_DRAGONROOST')):
					self.DragonLairs.append(index)