#from __future__ import unicode_literals # -*- coding: utf-8 -*- from __future__ import division import os from PyQt4.QtCore import * from PyQt4.QtGui import * from Terrain import Terrain class Case(QGraphicsPolygonItem): """objet graphique representant une case du plateau""" def __init__(self, plateau, parent=None): super(Case, self).__init__() #plateau self.plateau = plateau #attributs self.x = 0 self.y = 0 self.altitude = 0 self.terrain = Terrain() #terrain par defaut self.couleur = None #couleur du fond par defaut self.bordure = QColor(85, 85, 85, 85) #couleur de la bordure par defaut self.estDansChampDeplacement = False #la case est dans le champ de deplacement du pion selectionne self.centreGraphique = None self.formeCases = "H" self.occupeePar = {} #objet: altitudes occupees (sous forme de tuple, ex: (0,1,2) pour une creature occupant les cases d'altitude 0, 1 et 2) self.estCache = False #est sous un cache place par le MJ (cache le terrain, les decors, les pions aux joueurs...) self.ombre = True #ombre (cache les pions uniquement) #effet sur la case self.effetActif = "" #polygones d'affichage self.polygoneEffet = None self.polygoneAffichageSpecial = None self.etiquetteAltitude = None def __getstate__(self): """selectionne les attributs qui seront sauvegardes""" self.idTerrain = self.terrain.id state = {key:value for key, value in self.__dict__.items() if key in ["x", "y", "altitude","idTerrain","bordure","couleur", \ "estCache", "ombre", "effetActif"]} return (state) def __setstate__(self, state): """recupere les attributs sauvegardes""" self.__dict__ = state if self.idTerrain == "00": self.terrain = Terrain() else: self.terrain = charger("librairie\\terrain", self.idTerrain) ######## fonctions de base : geometrie, aspect graphique ###### def creer(self, x, y, couleur = QColor(0, 255, 0, 80)): """creation du polygone et enregistrement des donnees geometriques""" self.x = x self.y = y self.couleur = couleur #creation de l'objet graphique sur le plateau self.creerGraphique() def creerGraphique(self): """cree les objets graphiques representant la case""" #reinitialisation des variables self.couleurEffet = {"brule":QColor("orange"), "glace":QColor("white"), "poison":QColor("green"), "eau":QColor("blue")} self.imgEffet = {"brule":"effFeu.jpg", "glace":"effGlace.jpg", "poison":"effPoison.png", "eau":"effEau.png"} #enregistrement des cases voisines: self.voisins = self.lstVoisins(self.x, self.y) self.occupeePar = {} #enregistrement du centre if self.plateau.formeCases == "H": self.centreGraphique = QPointF(((self.x*0.866)+0.5773)*self.plateau.hCase, (self.y+0.5)*self.plateau.hCase) else: self.centreGraphique = QPointF((self.x+0.5)*self.plateau.hCase, (self.y+0.5)*self.plateau.hCase) #cree le polygone de la case self.setPolygon(self.polygone(self.x, self.y)) self.plateau.addItem(self) #interactions graphiques: self.setFlag(QGraphicsItem.ItemIsFocusable) self.setAcceptHoverEvents(True) self.setAcceptDrops(True) self.setZValue(0) self.setFiltersChildEvents(True) #pour afficher les coordonnees des cases: #text = QGraphicsSimpleTextItem("{}-{}".format(self.x,self.y), parent=self) #if self.plateau.formeCases == "H": # text.setPos(QPointF(((self.x*0.866)+0.2886)*self.plateau.hCase, self.y*self.plateau.hCase)) #else: # text.setPos(QPointF(self.x*self.plateau.hCase, self.y*self.plateau.hCase)) #polygone utilise lorsque dans champ de deplacement self.polygoneChampDep = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self) self.polygoneChampDep.setVisible(False) self.polygoneChampDep.setAcceptHoverEvents(False) #apparence initiale de la case self.majCouleur(self.couleur) #polygone utilise pour afficher la cible du curseur self.polygoneCible = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self) self.polygoneCible.setVisible(False) self.polygoneCible.setAcceptHoverEvents(False) #polygone test (pour capter les evts souris par dessus d'autres objets) ## self.polygoneTest = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self) ## self.polygoneTest.setVisible(True) ## self.polygoneTest.setAcceptHoverEvents(True) ## self.polygoneTest.setFlag(QGraphicsItem.ItemIsFocusable) ## self.polygoneTest.setBrush(QColor(0,0,0,0)) ## self.polygoneTest.setPen(QPen(QColor(0,0,0,0))) ## self.polygoneTest.setAcceptedMouseButtons(Qt.NoButton) ## self.polygoneTest.setZValue(200) ## self.polygoneTest.setParentItem(self) def majGraphique(self): """met a jour l'aspect graphique de la acse en fonction de son etat""" if self.terrain: if len(self.terrain.nom) > 0 : self.majTerrain(self.terrain) self.majAffichageSpecial("") self.majEffet(self.effetActif) def recreer(self, plateau): self.plateau = plateau super(Case, self).__init__() #polygones d'affichage self.polygoneEffet = None self.polygoneAffichageSpecial = None self.etiquetteAltitude = None self.creerGraphique() self.majGraphique() def polygone(self, x, y): """renvoie l'objet graphique hexagone de la case""" polygone = QPolygonF() if self.plateau.formeCases == "H": polygone << QPointF(((x*0.866)+0.2886)*self.plateau.hCase, y*self.plateau.hCase) \ << QPointF(((x*0.866)+0.866)*self.plateau.hCase, y*self.plateau.hCase) \ << QPointF(((x*0.866)+1.1547)*self.plateau.hCase, (y+0.5)*self.plateau.hCase) \ << QPointF(((x*0.866)+0.866)*self.plateau.hCase, (y+1)*self.plateau.hCase) \ << QPointF(((x*0.866)+0.2886)*self.plateau.hCase, (y+1)*self.plateau.hCase) \ << QPointF( (x*0.866)*self.plateau.hCase, (y+0.5)*self.plateau.hCase) else: polygone << QPointF(x*self.plateau.hCase, y*self.plateau.hCase) \ << QPointF((x+1)*self.plateau.hCase, y*self.plateau.hCase) \ << QPointF((x+1)*self.plateau.hCase, (y+1)*self.plateau.hCase) \ << QPointF(x*self.plateau.hCase, (y+1)*self.plateau.hCase) return polygone def lstVoisins(self, x, y): """renvoie la liste des cases voisines seulement cases existantes sur le plateau / seulement cases adjacentes (cas des cases carrees)""" voisins = [] if self.plateau.formeCases == "H": lst = [(x, y-1), (x+1, y-0.5), (x+1, y+0.5), (x, y+1), (x-1, y+0.5), (x-1, y-0.5)] else: lst = [(x, y-1), (x+1, y-1), (x+1, y), (x+1, y+1), (x, y+1), (x-1, y+1), (x-1, y), (x-1, y-1)] for coord in lst: if self.plateau.coordonneesValides(coord): voisins.append(coord) return voisins ######################## ########## fonctions liees a l'etat de la case ########### def majCouleur(self, couleur): self.couleur = couleur self.setBrush(couleur) self.polygoneChampDep.setBrush(self.couleurDep()) pinceau = QPen() pinceau.setColor(self.couleurBordure()) pinceau.setWidth(1) self.setPen(pinceau) def majTerrain(self, terrain): if terrain != None: self.terrain = terrain if len(self.terrain.imgTexture) > 0: self.setBrush(QBrush(QImage("img\\"+self.terrain.imgTexture))) else: if self.terrain.couleur.isValid(): self.setBrush(QBrush(self.terrain.couleur)) self.couleur = self.terrain.couleur self.polygoneChampDep.setBrush(self.couleurDep()) else: self.terrain = Terrain() def majOccupation(self, objet, nouveauZ = None): """met a jour l'occupation de la case par les pions, decors...""" if objet != None: if not objet in self.occupeePar and nouveauZ != None: #on ajoute l'objet a la liste des objets occupant la case ou on met a jour son altitude casesOccupees = [] for i in range(0, objet.hauteur): casesOccupees.append(self.altitude + nouveauZ + i) self.occupeePar[objet] = casesOccupees elif objet in self.occupeePar and nouveauZ == None: #on supprime l'objet de la liste des objets occupant la case del self.occupeePar[objet] else: pass def estOccupee(self, z=0): """renvoie vrai si la case correspondant a la hauteur z est occupee""" retour = False for objet in self.occupeePar: if objet.hauteur > 0 and z in self.occupeePar[objet]: retour = True return retour def estOccupeePar(self, z=0): """si la case correspondant a la hauteur z est occupee, renvoie l'objet en question sinon renvoie None""" retour = None for objet in self.occupeePar: if objet.hauteur > 0 and z in self.occupeePar[objet]: retour = objet return retour def pionOccupant(self): """si un pion occupe cette case, le renvoie""" retour = None for objet in self.occupeePar: if objet.__class__.__name__ == "Pion": retour = objet return retour def estFranchissable(self, z=0): """la case est-elle franchissable?""" retour = True if self.terrain.franchissable == False: retour = False else: if self.estOccupee(z) == True: retour = False return retour def estObstacleVision(self, hauteurObs): """renvoie vrai si la case et l'eventuel decor qui l'occupe bloquent le champ de vision d'un observateur situe a la hauteur precisee""" retour = False if self.altitude > hauteurObs: retour = True else: hauteurObstacle = 0 for objet in self.occupeePar: if objet.hauteur > hauteurObstacle: hauteurObstacle = objet.hauteur if (self.altitude + hauteurObstacle) > hauteurObs: retour = True return retour def majEstDansChampDeplacement(self, actif): """la case apparait ou pas comme etant dans le champ de deplacement d'un pion""" self.polygoneChampDep.setVisible(actif) def majEstCibleAttaque(self, actif): """la case apparait ou pas comme etant dans le champ de deplacement d'un pion""" self.polygoneChampDep.setVisible(actif) def majEstCibleCurseur(self, actif, valide=True): """affiche la case comme etant la cible du curseur (lors d'une creation de pion, d'un deplacement...)""" if actif: pinceau = QPen() pinceau.setWidth(5) brush = QBrush() brush.setStyle(13) pinceau.setColor(self.couleurProj(valide)) brush.setColor(self.couleurProj(valide)) self.polygoneCible.setPen(pinceau) self.polygoneCible.setBrush(brush) self.setZValue(99) else: self.setZValue(0) self.polygoneCible.setVisible(actif) def majEffet(self, effet): """met a jour l'effet actif sur la case""" if self.polygoneEffet == None: #on cree le polygone utilise pour afficher les effets self.polygoneEffet = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self) pinceau = QPen() pinceau.setColor(self.bordure) pinceau.setWidth(1) self.polygoneEffet.setPen(pinceau) self.polygoneEffet.setVisible(False) if len(effet) > 0 and effet != "aucun": #gradient de couleur gradient = QRadialGradient(self.centreGraphique, 0.5*self.plateau.hCase) couleur0 = QColor(0,0,0,0) couleur20 = self.couleurEffet[effet] couleur20.setAlpha(70) couleur10 = self.couleurEffet[effet] couleur10.setAlpha(255) gradient.setColorAt(0, couleur0) gradient.setColorAt(0.8, couleur0) gradient.setColorAt(0.95, couleur10) gradient.setColorAt(0.98, couleur20) gradient.setColorAt(1, couleur10) self.polygoneEffet.setBrush(gradient) self.polygoneEffet.setVisible(True) self.effetActif = effet else: self.polygoneEffet.setVisible(False) def majAltitude(self, altitude): """met a jour l'altitude de la case""" self.altitude = altitude def majAffichageSpecial(self, affichage=""): """donne a la case l'aspect demande en rendant visible/invisible le polygone d'affichage special""" #polygone d'affichage special (altitude, tactique...) if self.polygoneAffichageSpecial == None: self.polygoneAffichageSpecial = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self) pinceau = QPen() pinceau.setColor(self.bordure) pinceau.setWidth(1) self.polygoneAffichageSpecial.setPen(pinceau) if affichage != "altitude" and self.etiquetteAltitude: self.etiquetteAltitude.setVisible(False) if affichage == "tactique": if self.terrain.franchissable: self.polygoneAffichageSpecial.setBrush(QColor(255,255,255)) else: self.polygoneAffichageSpecial.setBrush(QColor(50,50,50)) self.polygoneAffichageSpecial.setVisible(True) elif affichage == "altitude": if self.etiquetteAltitude == None: self.etiquetteAltitude = QGraphicsSimpleTextItem("{}".format(self.altitude), parent=self) police = QFont("Georgia", 18) police.setItalic(True) self.etiquetteAltitude.setFont(police) self.etiquetteAltitude.setPos(QPointF(((self.x*0.866)+0.65)*self.plateau.hCase, (self.y+0.7)*self.plateau.hCase)) if self.altitude >= 0: couleur = QColor("red").lighter(200-(5*self.altitude)) else: couleur = QColor("purple").lighter(200+(5*self.altitude)) self.polygoneAffichageSpecial.setBrush(couleur) self.polygoneAffichageSpecial.setZValue(5) self.polygoneAffichageSpecial.setVisible(True) self.etiquetteAltitude.setText(QString.fromUtf8("{}".format(self.altitude))) self.etiquetteAltitude.setVisible(True) self.etiquetteAltitude.setZValue(6) else: self.polygoneAffichageSpecial.setVisible(False) def couleurDep(self): """renvoie une couleur secondaire utilisee pour les champs de deplacements""" luminositeActuelle = self.couleurEffective().lightness() if luminositeActuelle < 150: retour = QColor(210,255,255,100) else: retour = QColor(210,255,255,100) return retour def couleurProj(self, valide): """renvoie une couleur secondaire utilisee pour les projections de deplacement""" luminositeActuelle = self.couleurEffective().lightness() if valide: retour = QColor("white") else: retour = QColor("red") if luminositeActuelle > 220: retour = retour.darker(120) return retour def couleurBordure(self): """renvoie la couleur utilisee pour les bordures de la case""" luminositeActuelle = self.couleurEffective().lightness() if luminositeActuelle > 150: retour = QColor(85, 85, 85, 130) elif luminositeActuelle > 100 and luminositeActuelle <= 150: retour = QColor(10, 10, 10, 130) else: retour = QColor(255, 255, 255, 180) return retour def couleurEffective(self): """renvoie la couleur effective de la case (utile quand la texture est une image)""" texture = self.brush() if texture.textureImage().isNull(): couleurFond = texture.color() else: couleurFond = self.couleurDominante(texture.textureImage()) return couleurFond def couleurDominante(self, img): """retourne la couleur dominante d'une QImage""" r = 0 v = 0 b = 0 for i in range(0, img.width()): for j in range(0, img.height()): pixel = img.pixel(i,j) r += qRed(pixel) v += qGreen(pixel) b += qBlue(pixel) nb_pix = img.width() * img.height() r_moy = int(r / nb_pix) v_moy = int(v / nb_pix) b_moy = int(b / nb_pix) return QColor(r_moy, v_moy, b_moy) ######### evebements souris et clavier ################# #*EC def mousePressEvent(self, event): """evenement lors du clic souris""" super(Case, self).mousePressEvent(event) if event.button() == 1: #sur clic gauche accepte = self.plateau.caseCliquee((self.x, self.y)) if accepte: event.accept() else: event.ignore() def hoverEnterEvent(self, event): """met a jour l'affichage de la case au survol de la souris, si un pion est selectionne""" super(Case, self).hoverEnterEvent(event) self.plateau.caseSurvol(self) def hoverLeaveEvent(self, event): """met a jour l'affichage de la case au survol de la souris, si un pion est selectionne""" super(Case, self).hoverLeaveEvent(event) #self.plateau.caseSurvol() ########################