#########################################################################
#Titre: entite.py
#Depuis:14/01/2014
#A participé: Vivien, Nicolas
#
#Description: Fichier contenant les class des entitées du jeu
#
#Dernière modification: 08/05/2014 Vivien
#########################################################################
#----------------------------------------------------------------------
#Commentaires
#----------------------------------------------------------------------

#p=[fen(0),terrain(1),LMob(2),LTour(3),Chemin(4),Pause(5),Selection(6),Vie(7),
#Argent(8),chateau(9),PlacerSelect(10),LCase(11),LVague(12),CadreInfo(13),
#'TourBouton'(14),Stop(15),'CadreVague'(16),XP(17),cible(18),temps_passe(19),
#'affiche_temp'(20),'bouton_option'(21),gagner(22)]


#Importation des modules
from tkinter import *
from math import *
from random import *
from time import *

#Importation des inforamation
from info import *
def appel(fonction):
	def fonction_modifiee(*parametres_non_nommes, **parametres_nommes):
		print("On appelle {0}".format(fonction))
		return (fonction(*parametres_non_nommes, **parametres_nommes))
	return fonction_modifiee

class Mob:
	"""Class définissant les Mobs (monstres):
	Attributs:
		-coords x et y
		-chemin à empreinter
		-objets sur le cadre [ref](image+ligne de vie+cadre selection)
		-type,vitesse,vie
		-selected: statut sélectionné ou non
		(-viseur: tour qui le vise) =>peut etre à revoir

	Méthodes:
		-Etape: étapes du chemin (virage)
		-Move: déplacement de l'image
		-frapper: lancé quand le mob est frappé
		-select: lancé quand on clique sur le mob

	"""
	def __init__(self,t,p):
		"""init de la class mère"""
		
		#Cercle de ciblage
		p[2].append(self)
		
		# permet de savoir si deja affecté par un effet
		self.aff_v=0
		self.aff_p=0
		
		self.chemin=p[4][randrange(len(p[4]))].copy()
		#Coords
		self.x=self.chemin[0][0]
		self.y=self.chemin[0][1]
		
		self.chemin.pop(0)
		#Images
		self.img= p[1].create_image(self.x,self.y,image=IM[t]["img"])
		p[1].itemconfig(self.img,tag=self.img)
		
		self.ligne= [p[1].create_line(self.x-9,self.y+12,self.x+9,self.y+12,fill='green'),18]
		self.cadre=0
		self.cible=0
		#Caractères
		self.t=t #type de mob
		self.v=IM[t]["vitesse"]
		self.vie=IM[t]["vie"]
		
		#Système
		self.selected=0
		
		self.Etape(p)
		p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		
	def Etape (self,p):
		"""Méthode permettant le déplacement de l'objet"""
		if not p[5]:#si le jeu n'est pas en pause
			
			x= self.x-self.chemin[0][0]
			y= self.y-self.chemin[0][1]
			
			r=sqrt(x**2+y**2)
			
			x1=-x/r
			y1=-y/r
			
			self.Move(r,x1,y1,p)
			
		else: p[0].after(100,self.Etape,p)	
		
	def Move(self,r,x1,y1,p):
		if not p[5]:
			
			if self.vie>0:
				#On calcul les nouvelles coord.
				self.x+=self.v*x1
				self.y+=self.v*y1
				#Puis on déplace le point par rapport à A
				p[1].coords(self.img,self.x,self.y)
				p[1].coords(self.ligne[0],self.x-9,self.y+12,self.x-9+self.ligne[1],self.y+12)
				if self.selected:p[1].coords(self.cadre,self.x,self.y)
				
				
				if p[18]==self:
					#Si le mob est la cible alors faire bouger la cible
					p[1].coords(self.cible,self.x,self.y)

				
				if self.chemin[0][0]-self.v<self.x<self.chemin[0][0]+self.v and self.chemin[0][1]-self.v<self.y<self.chemin[0][1]+self.v:
					self.x=self.chemin[0][0]
					self.y=self.chemin[0][1]
					p[1].coords(self.img,self.x,self.y)
					p[1].coords(self.cadre,self.x,self.y)
					if self.selected:p[1].coords(self.ligne[0],self.x-9,self.y+12,self.x-9+self.ligne[1],self.y+12)
					
					self.chemin.pop(0)
					if len(self.chemin)==0:self.Exploser(p)
					else:self.Etape(p)
				else:p[0].after(30,self.Move,r,x1,y1,p)

		elif not p[15]:p[0].after(100,self.Move,r,x1,y1,p)
		
	def frapper (self,dommage,p):
		"""Méthode lancée quand le mob est frappé"""
		#On enlève la puissance du dommage à la vie du mob
		if self.vie>0:
			self.vie-=dommage
			r=self.vie/IM[self.t]["vie"]
			self.ligne[1]=ceil(r*18)
			if r>0.25 and r <=0.5:
				p[1].itemconfig(self.ligne[0],fill='orange')
			elif r<=0.25:
				p[1].itemconfig(self.ligne[0],fill='red')
			
			if self.selected==True :ConfigInfo[1][1].config(text='Vie: {} pts'.format(self.vie))
			
			if self.vie <=0:self.Mourir (p)
		
	def Mourir (self,p):
		if self.selected==True :
				for element in ConfigInfo[1]:
					element.grid_forget()
				ConfigInfo[1][0]=Label(p[13],text='Mob [Dead]',bg="#6b6b6b")
				ConfigInfo[1][0].grid(column=0,columnspan=2,row=0,sticky=W)
		p[8].set(p[8].get()+IM[self.t]["argent+"])
		p[1].delete(self.img)
		p[1].delete(self.ligne[0])
		if p[6]==self: p[1].delete(self.cadre)
		if self.cible!=0:
			p[1].delete(self.cible)
			p[18]=0

		self.AnimationExplosion(0,p[1].create_image(self.x,self.y,image=Explosion[0]),p)
		#Différence
		try:p[2].remove(self)
		except ValueError:pass
		# ajout XP
		p[17].set(p[17].get()+IM[self.t]["experience"])
		
		#Vérifications pour gagner
		if len (p[2])==0 and p[7].get() >0 and p[12][len(p[12])-1].Mob1==0 and p[12][len(p[12])-1].Mob2==0 and p[12][len(p[12])-1].Mob3==0 and p[12][len(p[12])-1].Mob4==0:
			p[22](p[0],p,p[23],p[24],p[25])
			
	def Exploser (self,p):
		self.v=0 # permet d'éviter aux effet de recreer une image
		if self.selected==True :
				for element in ConfigInfo[1]:
					element.grid_forget()
				ConfigInfo[1][0]=Label(p[13],text='Mob [Dead but kicked your ass]',bg="#6b6b6b")
				ConfigInfo[1][0].grid(column=0,columnspan=2,row=0,sticky=W)
		p[7].set(p[7].get()-1)
		p[1].delete(self.img)
		p[1].delete(self.ligne[0])
		if p[6]==self: p[1].delete(self.cadre)
		if self.cible!=0:
			p[1].delete(self.cible)
			p[18]=0

		self.AnimationExplosion(0,p[1].create_image(self.x,self.y,image=Explosion[0]),p)
		#Différence
		try:p[2].remove(self)
		except ValueError:pass
		
		#Vérifications pour gagner
		if len (p[2])==0 and p[7].get() > 0 and p[12][len(p[12])-1].Mob1==0 and p[12][len(p[12])-1].Mob2==0 and p[12][len(p[12])-1].Mob3==0 and p[12][len(p[12])-1].Mob4==0:
			p[22](p[0],p,p[23],p[24],p[25])
			
	def AnimationExplosion(self,i,img,p):

		if i<5:
			i+=1
			p[1].itemconfig(img,image=Explosion[i])
			p[0].after(80,self.AnimationExplosion,i,img,p)
		else:p[1].delete(img)
	
	def select(self,p):		
		if self.selected:
			EffaceConfig()
			self.selected=0
			p[1].delete(self.cadre)
			if p[6]==self:p[6]=0
		else:
			self.selected=True
			if p[6]!=0 and p[6].selected==1 and p[6]!=self :p[6].select(p)
			p[6]=self
			self.cadre= p[1].create_image(self.x,self.y,image=PSelec)
			p[1].tag_raise(self.img,ALL)
			if self.cible!=0: p[1].tag_raise(self.cible,ALL)
			self.infos(p)
	
	def effet_vitesse(self,p,effet,effet_duree,passage): # effet:attaque vitesse: vitesse initial passage: le thread doit s'arréter
		if not p[5]:
				
			if self.vie>0 and passage==0:
				self.aff_v+=1
				vitesse_ralenti=IM[self.t]["vitesse"]*effet
				if self.v > vitesse_ralenti:
					self.v=vitesse_ralenti
					p[1].delete(self.img)
					self.img= p[1].create_image(self.x,self.y,image=IM[self.t]["img_glace"])
					p[1].itemconfig(self.img,tag=self.img)
					p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
					
					p[0].after(effet_duree*1000,self.effet_vitesse,p,effet,effet_duree,1)
					
			elif self.vie>0 and passage==1:
				self.aff_v-=1
				if self.aff_v==0:
					self.v=IM[self.t]["vitesse"]
					p[1].delete(self.img)
					self.img= p[1].create_image(self.x,self.y,image=IM[self.t]["img"])
					p[1].itemconfig(self.img,tag=self.img)
					p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		else:p[0].after(100,self.effet_vitesse,p,effet,effet_duree,passage)

	def effet_poison(self,p,effet,effet_duree,passage): # effet:attaque vitesse: vitesse initial passage: le thread doit s'arréter
		if not p[5]:
			
			if passage==0 and self.vie>0:
				if self.aff_p==0:
					p[1].delete(self.img)
					self.img= p[1].create_image(self.x,self.y,image=IM[self.t]["img_feu"])
					p[1].itemconfig(self.img,tag=self.img)
					p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		
					self.frapper(effet,p) # enleve vie
				
				p[0].after(1000,self.effet_poison,p,effet,effet_duree,passage+1)			
				self.aff_p+=1
			
			elif passage==effet_duree  and self.vie>0:
				self.aff_p-=1
				if self.aff_p == 0:
					p[1].delete(self.img)
					self.img= p[1].create_image(self.x,self.y,image=IM[self.t]["img"])
					p[1].itemconfig(self.img,tag=self.img)
					p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
				
			elif self.vie>0:
				if self.aff_p==1:self.frapper(effet,p)
				p[0].after(1000,self.effet_poison,p,effet,effet_duree,passage+1)			
		else:p[0].after(100,self.effet_poison,p,effet,effet_duree,passage)

	def relancer(self,p):
		
		self.img= p[1].create_image(self.x,self.y,image=IM[self.t]["img"])
		p[1].itemconfig(self.img,tag=self.img)
		
		self.ligne= [p[1].create_line(self.x-9,self.y+12,self.x+9,self.y+12,fill='green'),18]
		self.cadre=0
		
		self.Etape(p)
		p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		if self.selected==1:
			self.selected=0
			self.select(p)
	
	def infos(self,p):
		"""Fonction permettant d'afficher les informations sur le mob sélectioné sur
		le Cadre Info (p[13]). Elle structure la Frame et enregistre les
		widget dans ConfigInfo pour qu'ils soient modifiables par les Mobs
		sélectionnés (ex: vie qui baisse)
		"""

		#Création et affichage dans l'ordre de:
		#-Le titre: ici "Mob [Alive]"
		#-La vie
		#-La vitesse (utile?)
		#-Le Niveau (par encore variable)
		#-Le Gain d'argent que procurre le mob en mourrant
		
		#Le titre
		ConfigInfo[1][0]=Label(p[13],text='Mob [Alive]',bg="#6b6b6b")
		ConfigInfo[1][0].grid(column=0,columnspan=2,row=0,sticky=W)
		#La vie
		ConfigInfo[1][1]=Label(p[13],text='Vie: {} pts'.format(self.vie),bg="#6b6b6b")
		ConfigInfo[1][1].grid(column=0,columnspan=2,row=1,sticky=W)
		#XP
		ConfigInfo[1][2]=Label(p[13],text='XP: {}'.format(IM[self.t]["experience"]),bg="#6b6b6b")
		ConfigInfo[1][2].grid(column=0,columnspan=2,row=2,sticky=W)
		#Le Gain
		ConfigInfo[1][3]=Label(p[13],text='Argent: {}'.format(IM[self.t]["argent+"]),bg="#6b6b6b")
		ConfigInfo[1][3].grid(column=0,columnspan=2,row=3,sticky=W)
		# cible
		if self.cible == 0:ConfigInfo[1][4]=Button(p[13],text='CIBLE !',command= lambda :self.ciblage(p))#,bg="#6b6b6b"
		else:ConfigInfo[1][4]=Button(p[13],text='CIBLE !',command= lambda :self.ciblage(p),state=DISABLED)
		ConfigInfo[1][4].grid(column=0,columnspan=2,row=4,sticky=W,padx=5)
	
	def ciblage (self,p):
		#On déselectionne l'ancienne cible si elle existe
		if p[18]!=0:
			p[1].delete(p[18].cible)
			p[18].cible=0
		ConfigInfo[1][4].config(state=DISABLED)
		p[18]=self
		self.cible=p[1].create_image(self.x,self.y,image=cible)
		p[1].tag_bind(self.cible, "<Button-1>", lambda event: self.select(p))


		
def configurer_infos_upgrade (row,i1,i2,i3,mess,caractere_name,caractere,lvlcaractere,methode_upgrade,p):
	
	#=>Affichage du caractère de la tour
	ConfigInfo[0][i1]=Label(p[13],text=mess.format(caractere),bg="#6b6b6b")
	ConfigInfo[0][i1].grid(column=0,columnspan=2,row=row,sticky=W)
	#=>Affichage de la barre de niveau
	ConfigInfo[0][i2]=Label(p[13],image=BN[lvlcaractere],bg="#6b6b6b")
	ConfigInfo[0][i2].grid(column=0,columnspan=1,row=row+1,sticky=W)
	#=>Affichage du bouton Upgrade
	if lvlcaractere< 6:ConfigInfo[0][i3]=Button(p[13],text='Upgrade: {}'.format(UpgradeInfo[caractere_name][lvlcaractere][1]),command=lambda: methode_upgrade(p))
	#Si pas niveau max (6) alors bouton activé
	else:ConfigInfo[0][i3]=Button(p[13],text='Max !',state=DISABLED)
	#Sinon bouton désactivé
	ConfigInfo[0][i3].grid(column=1,columnspan=1,row=row+1,sticky=W)

def configurer_upgrade (i1,i2,i3,mess,valeur,caractere_name,caractere,lvlcaractere,p):
	#On augmente la valeur de la tour de la moitié du prix de l'amélioration
	valeur+=ceil(UpgradeInfo[caractere_name][lvlcaractere][1]/2)
	
	#On change la valeur du rayon avec celle de l'amélioration
	caractere=UpgradeInfo[caractere_name][lvlcaractere][0]
	lvlcaractere+=1#on augmente le niveau du rayon
	
	#====== Actualisation de l'affichage ======
	
	if lvlcaractere==6:ConfigInfo[0][i3].config(text="Max !",state=DISABLED)
	#Si on attend le niveau max on désactive le bouton
	else:ConfigInfo[0][i3].config(text="Upgrade: {}".format(UpgradeInfo[caractere_name][lvlcaractere][1]))
	#Sinon on met le prix de la prochaine amélioration dans le bouton upgrade
	
	#Puis on actualise l'affichage du bouton vendre
	ConfigInfo[0][10].config(text='Vendre: {}'.format(valeur))
	#On actualise la valeur du rayon
	ConfigInfo[0][i1].config(text=mess.format(caractere))
	#Et l'image correspondant au niveau
	ConfigInfo[0][i2].config(image=BN[lvlcaractere])
	
	#====== Prélèvement de l'argent =====
	
	#On enlève le prix de l'amélioration à argent (p8) en dernier pour lancer la vérification sur les
	#boutons
	p[8].set(p[8].get()-UpgradeInfo[caractere_name][lvlcaractere-1][1])
	
	return (valeur,caractere,lvlcaractere)

	
def Tour(x,y,t,p):
	if t==1:return Tour1(x,y,p)
	elif t==2:return Tour2(x,y,p)
	elif t==3:return Tour3(x,y,p)
	
class Tour_modele:
	"""Class définissant l'objet tour gérant tous son mécanisme
	detection,orientation,tire,etc..)
	"""
	

	def __init__(self,x,y,t,p):
		"""Méthode d'initialisation de la class tour:
		création des attributs de l'objet
		"""
		
		self.img=p[1].create_image(x,y,image=IT[t]["image"]) # creation image
		p[1].itemconfig(self.img,tag=self.img) # ajout de tags
		
		self.cadre= p[1].create_image(x,y,image=PSelec) # creation de son cadre
		p[1].tag_lower(self.cadre,ALL)
		
		self.canon=p[1].create_image(x,y,image=Canon[0])
		p[1].tag_raise(self.canon,ALL)
		self.angle=0
		
		self.selected=0 # permet de savoir s'elle est selectionne ou non
		
		self.t=t # type
		self.rayon =IT[t]["rayon"]
		self.lvlrayon=0

		self.x=x 
		self.y=y# decide si la tour attaque ou non
		
		self.cercle=0
		self.valeur=ceil(IT[t]["prix"]/2)
		
		p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p)) # permet de tagger la tour
		p[1].tag_bind(self.canon, "<Button-1>", lambda event: self.select(p))

	def select(self,p):
		"""Méthode lancé quand l'utilisateur clique sur la tour ou sur 
		son canon ou quand un autre objet est sélectionné
		"""
		if self.selected:
			EffaceConfig()
			self.selected=0
			p[1].lower(self.cadre,ALL)
			p[1].delete(self.cercle)
			if p[6]==self:p[6]=0
		else:
			self.selected=1
			if p[6]!=0 and p[6].selected==1 and p[6]!=self :p[6].select(p)
			p[6]=self
			p[1].tag_raise(self.cadre,ALL)
			p[1].tag_raise(self.img,ALL)
			p[1].tag_raise(self.canon,ALL)
			self.cercle=p[1].create_oval(self.x-self.rayon,self.y-self.rayon,self.x+self.rayon,self.y+self.rayon,width=1,outline="#950e08")#couleur =rouge
			self.infos(p)
			
	def detection(self,p):
		"""Méthode permettant à la tour de trouver un mob qui
		est dans son rayon d'attaque
		"""

		if not p[5]:
			
			if p[18] != 0 and (p[18].x-self.x)**2+(p[18].y-self.y)**2<self.rayon**2:
					self.PreOrientation(p[18],p)
					
			else:
				shuffle(p[2])
				for mob in p[2]:
					if(mob.x-self.x)**2+(mob.y-self.y)**2<self.rayon**2:
						self.PreOrientation(mob,p)
						break
			        
		elif not p[15]:p[0].after(100,self.detection,p)
		
	def Orientation (self,diff,mob,p):
		"""Méthode lancée en boucle pour changer l'image du canon pour
		que l'angle d'orientation du canon corresponde à l'angle de tire
		"""
		if not p[5]:
			
			if diff==0:
				if mob!=0:
					x=mob.x-self.x
					y=mob.y-self.y
					
					r=sqrt(x**2+y**2)
					
					x1=x/r
					y1=y/r
					ID_Boulet=p[1].create_image(self.x,self.y,image=BFeu)
					p[1].tag_lower(ID_Boulet,self.canon)
					self.boulet(0,r,x1,y1,ID_Boulet,mob,p)
					self.occupe=0
			else:
				if diff>0:
					self.angle+=1
					diff-=1
		
				elif diff<0:
					self.angle-=1
					diff+=1
				if self.angle==64:self.angle=0
				elif self.angle==-1:self.angle=63
				p[1].itemconfig(self.canon,image=Canon[int(self.angle)])
				p[0].after(1,self.Orientation,diff,mob,p)
				
		elif not p[15]:p[0].after(100,self.Orientation,diff,mob,p)
		
	def PreOrientation(self,mob,p):
		"""Méthode calculant la différence d'angle entre l'angle du canon
		et l'angle de tire
		"""
		if (not p[5]) and mob!=0:
			
	
			if (mob.x-self.x)**2+(mob.y-self.y)**2<self.rayon**2:

				x=mob.x-self.x
				y=mob.y-self.y
				
				r=sqrt(x**2+y**2)
				
				x1=x/r
				y1=y/r
				angle=0
				for i in range(17):
					if LAngle[i]>=fabs(x1)>LAngle[i+1]:angle=i
				if x1<0:angle=32-angle
				if y1>0:angle=64-angle
				if angle==64:angle=0
				
				diff=angle-self.angle
				if diff>=32:diff-=64
				elif diff<=-32:diff+=64
				self.Orientation(diff,mob,p)
				
		elif not p[15]:p[0].after(100,self.PreOrientation,p)
		
	def boulet(self,i,r,x1,y1,ID_Boulet,mob,p):

		if not p[5]:
			
			#On calcul les nouvelles coord.
			v=VB
			i+=v
			x=i*x1
			y=i*y1
			#Puis on déplace le point par rapport à A
			p[1].coords(ID_Boulet,self.x+x,self.y+y)
			
			if r-v<i<r+v or r-v>i>r+v:
				p[1].coords(ID_Boulet,self.x+r*x1,self.y+r*y1)
				self.toucher(mob,p)
				p[1].delete(ID_Boulet)
				
			else:
				p[0].after(10,self.boulet,i,r,x1,y1,ID_Boulet,mob,p)

		elif not p[15]:p[0].after(100,self.boulet,i,r,x1,y1,ID_Boulet,mob,p)
		
	def toucher(self,mob,p):pass
		
	def UpgradeRayon(self,p):
		"""
		Méthode appellée pour augmenter le niveaux de rayon de la tour
		Elle est appellée depuis le bouton Upgrade du rayon ConfigInfo[0][3]
		"""
		
		self.valeur,self.rayon,self.lvlrayon=configurer_upgrade (1,2,3,"Rayon: {} px",self.valeur,'rayon',self.rayon,self.lvlrayon,p)
		
		#Et on actualise le cercle représentant le rayon de la tour
		p[1].coords(self.cercle,self.x-self.rayon,self.y-self.rayon,self.x+self.rayon,self.y+self.rayon)

	def Vendre (self,p):
		p[8].set(p[8].get()+self.valeur)
		x=int((self.x+15)/29)
		y=int((self.y+15)/29)
		rang=x*21+y
		p[11][rang].Vider(p)
		p[1].delete(self.canon)
		p[1].delete(self.cadre)
		p[1].delete(self.img)
		p[1].delete(self.cercle)
		p[3].remove (self)
		EffaceConfig()
		p[6]=0

	def relancer(self,p):
		
		self.img=p[1].create_image(self.x,self.y,image=IT[self.t]["image"]) # creation image
		p[1].itemconfig(self.img,tag=self.img) # ajout de tags
		
		self.cadre= p[1].create_image(self.x,self.y,image=PSelec) # creation de son cadre
		p[1].tag_lower(self.cadre,ALL)
		
		self.canon=p[1].create_image(self.x,self.y,image=Canon[0])
		p[1].tag_raise(self.canon,ALL)
		
		p[1].tag_bind(self.img, "<Button-1>", lambda event: self.select(p)) # permet de tagger la tour
		p[1].tag_bind(self.canon, "<Button-1>", lambda event: self.select(p))
		
		if self.selected==1:
			self.selected=0
			self.select(p)
			
	def infos(self,p):
		"""Fonction permettant d'afficher les informations et les améliorations
		disponibles sur la tour sélectionnée sur le Cadre Info (p13).
		Elle structure la Frame et enregistre les widgets dans ConfigInfo pour
		qu'ils soient modifiables par les Tours sélectionnés (ex: UpgradeRayon)
		"""

		#====== On crée un titre à la fiche d'infos ======
		
		ConfigInfo[0][0]=Label(p[13],font=style1,text='Tour',bg="#6b6b6b")
		ConfigInfo[0][0].grid(column=0,columnspan=2,row=0,sticky=W)
		
		#====== Partie du rayon ======
		
		configurer_infos_upgrade (1,1,2,3,"Rayon: {} px",'rayon',self.rayon,self.lvlrayon,self.UpgradeRayon,p)
		
		#====== Partie pour vendre la tour ======
		ConfigInfo[0][10]=Button(p[13],text='Vendre: {}'.format(self.valeur),command=lambda: self.Vendre(p))
		ConfigInfo[0][10].grid(column=0,columnspan=2,row=7,sticky=W,padx=5)
		
		#On lance une vérification de prix au cas ou l'argent de s'actualise pas entre temps
		#ce qui rendrait les boutons accessibles sans l'argent nécessaire
		VerificationPrix(p)

		
class Tour1 (Tour_modele):
	""" Tour ayant seuleument l'effet dommage"""
	
	def __init__ (self,x,y,p):
		""" constructeur de la class """
		#On lance d'abord le constructeur commun à toute les tours
		Tour_modele.__init__(self,x,y,1,p)
		# Puis on crée les attributs spécifiques
		self.dommage=IT[1]["effet_dommage"]
		self.lvldommage=0
		
	def infos (self,p):
		
		#====== Partie du dommage ======
		configurer_infos_upgrade (3,4,5,6,'Dommage: {} pts','dommage',self.dommage,self.lvldommage,self.UpgradeDommage,p)
		
		#on affiche les infos communes à toutes les tours
		Tour_modele.infos(self,p)
	def UpgradeDommage(self,p):
	
		self.valeur,self.dommage,self.lvldommage=configurer_upgrade (4,5,6,'Dommage: {} pts',self.valeur,'dommage',self.dommage,self.lvldommage,p)
		
	def toucher(self,mob,p):
		mob.frapper (self.dommage,p)

class Tour2 (Tour_modele):
	""" Tour ayant l'effet ralentissement"""
	
	def __init__ (self,x,y,p):
		""" constructeur de la class """
		#On lance d'abord le constructeur commun à toute les tours
		Tour_modele.__init__(self,x,y,2,p)
		# Puis on crée les attributs spécifiques
		self.vitesse=IT[2]["effet_vitesse"]
		self.lvlvitesse=0
		self.duree=IT[2]["duree_effet"]
		self.lvlduree=0
		
	def infos (self,p):
		
		
		#====== Partie de la vitesse ======
		configurer_infos_upgrade (3,4,5,6,'Intensité ralentissement: {} ','vitesse',self.vitesse,self.lvlvitesse,self.UpgradeVitesse,p)
		#====== Partie de la duree ======
		configurer_infos_upgrade (5,7,8,9,"Duree d'effet: {}s",'duree_vitesse',self.duree,self.lvlduree,self.UpgradeDuree,p)
		
		#on affiche les infos communes à toutes les tours
		Tour_modele.infos(self,p)
		
	def UpgradeVitesse(self,p):
	
		self.valeur,self.vitesse,self.lvlvitesse=configurer_upgrade (4,5,6,'Intensité ralentissement: {} ',self.valeur,'vitesse',self.vitesse,self.lvlvitesse,p)
		
	def UpgradeDuree(self,p):
	
		self.valeur,self.duree,self.lvlduree=configurer_upgrade (7,8,9,"Duree d'effet: {}s",self.valeur,'duree_vitesse',self.duree,self.lvlduree,p)
		
	def toucher(self,mob,p):
		mob.effet_vitesse(p,self.vitesse,self.duree,0)
		
class Tour3 (Tour_modele):
	""" Tour ayant l'effet poison"""
	
	def __init__ (self,x,y,p):
		""" constructeur de la class """
		#On lance d'abord le constructeur commun à toute les tours
		Tour_modele.__init__(self,x,y,3,p)
		# Puis on crée les attributs spécifiques
		self.poison=IT[3]['effet_poison']
		self.lvlpoison=0
		self.duree=IT[3]["duree_effet"]
		self.lvlduree=0
		
	def infos (self,p):
		
		#====== Partie de la vitesse ======
		configurer_infos_upgrade (3,4,5,6,'dommage par seconde: {} ','poison',self.poison,self.lvlpoison,self.UpgradePoison,p)
		#====== Partie de la duree ======
		configurer_infos_upgrade (5,7,8,9,"Duree d'effet: {}s",'duree_poison',self.duree,self.lvlduree,self.UpgradeDuree,p)
		
		#on affiche les infos communes à toutes les tours
		Tour_modele.infos(self,p)
		
	def UpgradePoison(self,p):
	
		self.valeur,self.poison,self.lvlpoison=configurer_upgrade (4,5,6,'dommage par seconde: {} ',self.valeur,'poison',self.poison,self.lvlpoison,p)
		
	def UpgradeDuree(self,p):
	
		self.valeur,self.duree,self.lvlduree=configurer_upgrade (7,8,9,"Duree d'effet: {}s",self.valeur,'duree_poison',self.duree,self.lvlduree,p)
		
	def toucher(self,mob,p):
		if mob.aff_p==0:mob.effet_poison(p,self.poison,self.duree,0)
		

# mob.frapper(self.dommage,p)
# if mob.affV==False and self.eff_vitess!=0:# lance effet vitesse
# mob.effet_vitesse(p,self.eff_vitess,0,0)
# mob.affV=True
# elif mob.affD==False and self.eff_pois!=0:# lance effet poison
# mob.effet_poison(p,self.eff_pois,0)
# mob.affD=True
	
#self.dommage=IT[t]["dommage"] # dommage
#self.lvldommage=1
#self.eff_vitess=IT[t]["effet_vitess"]
#self.eff_pois=IT[t]["effet_pois"]
		
class Vague:
	"""Class qui permet de gérer les vagues de monstres et l'affichage du cadre
	vague
	"""

	def __init__(self):
		#info de création de l'objet déterminé dans Editeur de Carte
		self.nom="SansNom"
		self.rang=0
		
		self.Mob1=0
		self.lvlMob1=1

		self.Mob2=0
		self.lvlMob2=1
		
		self.Mob3=0
		self.lvlMob3=1

		self.Mob4=0
		self.lvlMob4=1
		
		self.duree=60
		
		self.frequence=0
		self.liste=[] 
		
		#variables utilisée au lancement de la vague dans le jeu
		self.GlobalDuree=0
		self.DureeFixe=0
		
		self.img=0
		self.label=0
		self.x=0
		#y=0 tjrs
		
		self.selected=0
		
	
	def lancer (self,p,CadreVague):
		for vague in p[12][:self.rang]:
			self.GlobalDuree+=vague.DureeFixe
		self.x=self.GlobalDuree*2
		self.img=CadreVague.create_rectangle(self.x,0,self.x+self.DureeFixe*2,49,fill=LCouleur[self.rang])
		CadreVague.itemconfig(self.img,tag=self.img)
		CadreVague.tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		self.Boucle(p,CadreVague)

	def Boucle (self,p,CadreVague):
		if not p[5]:
			
			if self.x>-self.DureeFixe*2:
				if self.x==0:self.BoucleMob(p)
				if self.x<=0:self.duree-=0.5
				self.x-=1
				self.GlobalDuree-=0.5
				CadreVague.coords(self.img,self.x,0,self.x+self.DureeFixe*2,49)
				
				p[0].after(500,self.Boucle,p,CadreVague)
			
		elif not p[15]:p[0].after(100,self.Boucle,p,CadreVague)
			
	def BoucleMob (self,p):
		# Verification, Type Mob toujours existant sinon supprimme
		if not p[5]:
			
			if self.liste!=[]:
				t=self.liste[randrange(len(self.liste))]
				
				if t ==1:
					self.Mob1-=1
					Mob(t,p)
					if self.selected:ConfigInfo[2][2].config(text=str(self.Mob1))
					if self.Mob1==0:
						self.liste.remove(1)
						self.verif_fin(p)
				if t ==2:
					self.Mob2-=1
					Mob(t,p)
					if self.selected:ConfigInfo[2][4].config(text=str(self.Mob2))
					if self.Mob2==0:
						self.liste.remove(2)
						self.verif_fin(p)
				if t ==3:
					self.Mob3-=1
					Mob(t,p)
					if self.selected:ConfigInfo[2][6].config(text=str(self.Mob3))
					if self.Mob3==0:
						self.liste.remove(3)
						self.verif_fin(p)
				if t ==4:
					self.Mob4-=1
					Mob(t,p)
					if self.selected:ConfigInfo[2][8].config(text=str(self.Mob4))
					if self.Mob4==0:
						self.liste.remove(4)
						self.verif_fin(p)

				# Mob(t,p)# creation du mob
				p[0].after(self.frequence,self.BoucleMob,p)
		elif not p[15]:p[0].after(100,self.BoucleMob,p)
		
	def select(self,p):		
		if self.selected:
			EffaceConfig()
			self.selected=0
			if p[6]==self:p[6]=0
		else:
			self.selected=True
			if p[6]!=0 and p[6].selected==1 and p[6]!=self :p[6].select(p)
			p[6]=self
			InfoVague(p,self)
			
	def relancer(self,p):
		if self.selected==1:
			self.selected=0
			self.select(p)
		self.img=p[16].create_rectangle(self.x,0,self.x+self.DureeFixe*2,50,fill=LCouleur[self.rang])
		p[16].itemconfig(self.img,tag=self.img)
		p[16].tag_bind(self.img, "<Button-1>", lambda event: self.select(p))
		self.Boucle(p,p[16])
		if self.x<=0:self.BoucleMob(p)
	
	def avance_rapide (self,long,p):
		self.x-=long
		if self.x>-self.DureeFixe*2:
			if self.x==0:self.BoucleMob(p)
			if self.x<=0:self.duree-=0.5
			self.GlobalDuree-=0.5*long
			p[16].coords(self.img,self.x,0,self.x+self.DureeFixe*2,50)
		
	def passer (self,p):
		for vague in p[12][self.rang+1:]:
			vague.avance_rapide(self.x+self.DureeFixe*2,p)
		self.x-=self.x+self.DureeFixe*2
		self.GlobalDuree-=0.5*(self.x+self.DureeFixe*2)
		p[16].coords(self.img,self.x,0,self.x+self.DureeFixe*2,50)
		ConfigInfo[2][11].config(state=DISABLED)
	
	def verif_fin(self,p):
		if self.rang<len(p[12])-1 and self.Mob1==0 and self.Mob2==0 and self.Mob3==0 and self.Mob4==0 and p[12][self.rang+1].selected: ConfigInfo[2][11].config(state=ACTIVE)


class Case:

	def __init__ (self,x,y,t,p):
		#x et y en coordonnées dee cases pas de px
		self.x=x
		self.y=y
		
		self.CaseOccupe=0
		self.t=t
		self.sol=p[1].create_image(x*29-15, y*29-15, image=ICase[t])
		self.vide=p[1].create_image(x*29-15, y*29-15, image=CaseVide,tag=self.sol)
		p[1].tag_lower(self.vide,ALL)
		
		if t=='chateau':
			p[1].tag_raise(self.sol,ALL)
			p[9]=self.sol
		elif p[9] != 'chateau' :p[1].tag_lower(self.sol,p[9])
		if t==0 :
			p[1].tag_bind(self.vide, "<Button-1>", lambda event: self.Placer(p))
	def PrepPlacer (self,p):
		if not p[5]:
			p[1].tag_raise(self.vide,ALL)
		elif not p[15]:p[0].after(100,self.Placer,p)
		
	def Placer (self,p):
		if not p[5]:
			p[1].tag_unbind(self.vide, "<Button-1>")
			p[3].append(Tour(self.x*29-15,self.y*29-15,p[10].get(),p))
			p[8].set(p[8].get()-IT[p[10].get()]["prix"])
		elif not p[15]:p[0].after(100,self.Placer,p)
	
	def FinPlacer (self,p):
		if not p[5]:
			p[1].tag_lower(self.vide,ALL)
		elif not p[15]:p[0].after(100,self.Placer,p)
		
	#Différence
	def Vider (self,p):
		p[1].tag_bind(self.vide, "<Button-1>", lambda event: self.Placer(p))
		self.CaseOccupe=0
		
	def relancer(self,p):
		self.sol=p[1].create_image(self.x*29-15, self.y*29-15, image=ICase[self.t])
		self.vide=p[1].create_image(self.x*29-15, self.y*29-15, image=CaseVide,tag=self.sol)
		p[1].tag_lower(self.vide,ALL)
		
		if self.t=='chateau':
			p[1].tag_raise(self.sol,ALL)
			p[9]=self.sol
		elif p[9] != 'chateau' :p[1].tag_lower(self.sol,p[9])
		if self.t==0 and self.CaseOccupe==0:
			p[1].tag_bind(self.vide, "<Button-1>", lambda event: self.Placer(p))
			
####################################################################
## Fonctions affichage
####################################################################

#ConfigInfo contient les widget du cadre info
ConfigInfo=[[0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0]]


	
def InfoVague(p,vague):

	ConfigInfo[2][0]=Label(p[13],text='Vague: {}'.format(vague.nom),bg="#6b6b6b")
	ConfigInfo[2][0].grid(column=0,columnspan=2,row=0,sticky=W)
	
	ConfigInfo[2][1]=Label(p[13],image=IM[1]["img"],bg="#6b6b6b")
	ConfigInfo[2][1].grid(column=0,columnspan=1,row=1,sticky=W)
	ConfigInfo[2][2]=Label(p[13],text=str(vague.Mob1),bg="#6b6b6b")
	ConfigInfo[2][2].grid(column=1,columnspan=1,row=1,sticky=W)
	
	ConfigInfo[2][3]=Label(p[13],image=IM[2]["img"],bg="#6b6b6b")
	ConfigInfo[2][3].grid(column=0,columnspan=1,row=2,sticky=W)
	ConfigInfo[2][4]=Label(p[13],text=str(vague.Mob2),bg="#6b6b6b")
	ConfigInfo[2][4].grid(column=1,columnspan=1,row=2,sticky=W)
	
	ConfigInfo[2][5]=Label(p[13],image=IM[3]["img"],bg="#6b6b6b")
	ConfigInfo[2][5].grid(column=0,columnspan=1,row=3,sticky=W)
	ConfigInfo[2][6]=Label(p[13],text=str(vague.Mob3),bg="#6b6b6b")
	ConfigInfo[2][6].grid(column=1,columnspan=1,row=3,sticky=W)
	
	ConfigInfo[2][7]=Label(p[13],image=IM[4]["img"],bg="#6b6b6b")
	ConfigInfo[2][7].grid(column=0,columnspan=1,row=4,sticky=W)
	ConfigInfo[2][8]=Label(p[13],text=str(vague.Mob4),bg="#6b6b6b")
	ConfigInfo[2][8].grid(column=1,columnspan=1,row=4,sticky=W)
	
	ConfigInfo[2][9]=Button(p[13],text='<<<',command=lambda: p[12][vague.rang-1].select(p))
	ConfigInfo[2][9].grid(column=0,columnspan=2,row=5,sticky=W,pady=5,padx=5)
	if vague.rang==0:ConfigInfo[2][9].config(state=DISABLED)
	
	ConfigInfo[2][10]=Button(p[13],text='>>>',command=lambda: p[12][vague.rang+1].select(p))
	ConfigInfo[2][10].grid(column=3,columnspan=1,row=5,sticky=E,pady=5,padx=5)
	if vague.rang==len(p[12])-1:ConfigInfo[2][10].config(state=DISABLED)
	
	ConfigInfo[2][11]=Button(p[13],text='Passer la vague',command=lambda: vague.passer(p))
	ConfigInfo[2][11].grid(column=0,columnspan=3,row=6,pady=5,padx=5)
	if  vague.x <= -vague.DureeFixe*2 or vague.x>0 or ( vague.rang!=0 and(p[12][vague.rang-1].Mob1!=0 or p[12][vague.rang-1].Mob2!=0 or p[12][vague.rang-1].Mob3!=0 or p[12][vague.rang-1].Mob4!=0)) or vague.rang==len(p[12])-1 :ConfigInfo[2][11].config(state=DISABLED)
	
def EffaceConfig():
	#Supression des objets qui pourraient etre affichés 
	#condition pour éviter erreur quand ConfigInfo contient que des 0
	for element in ConfigInfo[0]:
		if element!=0:element.destroy()
	for element in ConfigInfo[1]:
		if element!=0:element.destroy()
	for element in ConfigInfo[2]:
		if element!=0:element.destroy()
		


def VerificationPrix(p):
	"""Fonction lancée à chaque actualisation de l'argent. Elle rend accessible
	ou non les achats possibles"""
	if IT[1]["prix"]>p[8].get():
		p[14][0].config(state=DISABLED)
		p[0].unbind_all("1")
	else:
		p[14][0].config(state=ACTIVE)
		p[0].bind_all("1",lambda event:PlacerTourBis(p,1))
	if IT[2]["prix"]>p[8].get():
		p[14][1].config(state=DISABLED)
		p[0].unbind_all("2")
	else:
		p[14][1].config(state=ACTIVE)
		p[0].bind_all("2",lambda event:PlacerTourBis(p,2))
	if IT[3]["prix"]>p[8].get():
		p[14][2].config(state=DISABLED)
		p[0].unbind_all("3")
	else:
		p[14][2].config(state=ACTIVE)
		p[0].bind_all("3",lambda event:PlacerTourBis(p,3))
		
	t=type(p[6])
	if t == Tour1 or t == Tour2 or t == Tour3:
		#S'il la sélection est une tour:
		if p[6].lvlrayon<6:
			#Si son niveau de rayon est inférieur à 7 et si le prix est sup à l'argent
			if UpgradeInfo["rayon"][p[6].lvlrayon][1]>p[8].get():
				ConfigInfo[0][3].config(state=DISABLED)
			else:ConfigInfo[0][3].config(state=ACTIVE)
		if p[6].t==1 and p[6].lvldommage<6:
			#Si c'est une tour1 son niveau de dommage est inférieur à 7 et si le prix est sup à l'argent
			if UpgradeInfo["dommage"][p[6].lvldommage][1]>p[8].get():
				ConfigInfo[0][6].config(state=DISABLED)
			else:ConfigInfo[0][6].config(state=ACTIVE)
		if p[6].t==2:
			if p[6].lvlvitesse<6:
				if UpgradeInfo["vitesse"][p[6].lvlvitesse][1]>p[8].get():
					ConfigInfo[0][6].config(state=DISABLED)
				else:ConfigInfo[0][6].config(state=ACTIVE)
			if p[6].lvlduree<6:
				if UpgradeInfo["duree_vitesse"][p[6].lvlduree][1]>p[8].get():
					ConfigInfo[0][9].config(state=DISABLED)
				else:ConfigInfo[0][9].config(state=ACTIVE)
		if p[6].t==3:
			if p[6].lvlpoison<6:
				if UpgradeInfo["poison"][p[6].lvlpoison][1]>p[8].get():
					ConfigInfo[0][6].config(state=DISABLED)
				else:ConfigInfo[0][6].config(state=ACTIVE)
			if p[6].lvlduree<6:
				if UpgradeInfo["duree_poison"][p[6].lvlduree][1]>p[8].get():
					ConfigInfo[0][9].config(state=DISABLED)
				else:ConfigInfo[0][9].config(state=ACTIVE)
		
			
	
def Charge_terrain(carte,p):
	for x in range(23):
		for y in range(21):
			t=carte["Sol"][(x,y)]
			Case(x,y,t,p)

def PlacerTour(p,tour):	
	# recuperation(p,2)
	for case in p[11]:
		case.PrepPlacer(p)
	p[0].bind_all("<Button-1>",lambda event:FinPlacerTour(p,tour))

def PlacerTourBis(p,nb):
	p[10].set(nb)
	PlacerTour(p,nb)
		
def FinPlacerTour(p,tour):
	p[14][tour-1].deselect()
	for case in p[11]:
		case.FinPlacer(p)
	p[0].unbind_all("<Button-1>")
	