| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- #from __future__ import unicode_literals
- # -*- coding: utf-8 -*-
- from PyQt4.QtCore import QPointF, Qt, QRectF, QString
- from PyQt4.QtGui import QGraphicsItem, QColor, QGraphicsPolygonItem, QPen, \
- QGraphicsDropShadowEffect, QGraphicsPixmapItem, QGraphicsSimpleTextItem, \
- QFont, QGraphicsObject
- from Forme import Forme
- from lib import dmF
- from lib.rsc import RImage
- class Pion(QGraphicsObject):
- """pion du plateau de combat"""
- def __init__(self, parent=None):
- super(Pion, self).__init__()
- #caracteristiques du pion
- self.numero = -1
- self.pion = PolygonePion()
- self.couleur = QColor(200, 200, 200)
- self.logo = RImage()
- self.img = ImgPion()
- self.etiquette = EtiquettePion()
- self.brillance = BrillancePion()
- #infos liees au plateau (forme et position)
- self.plateau = None
- self.numComplementaire = "" #numero complementaire si necessaire
- #(si plusieurs pions portent le meme nom)
- self.position = (-1, -1)
- self.zR = 0 #altitude relative du pion par rapport aux cases qu'il occupe (vol slt)
- self.h = 1
- self.hMax = False #[decors slt] la hauteur doit etre calculee pour atteindre le plafond (s'il existe)
- self.posPile = 0 #position du pion dans la pile des pions (pour l'ordre de re-creation des pions)
- self._place = False #le pion est place sur le plateau
-
- self.forme = None
- self.formeDef = {"H":[], "C":[]}
- self.etat = 0 #de 0 (indemne) a 4 (mort ou hors combat)
- self.statuts = []
- #objets et parametres graphiques
- self.etiquetteGraphique = None
- self.nbRotations = 0
- def __getstate__(self):
- nePasSvg = ["_place", "plateau", "brillance", "pion", "etiquetteGraphique"]
- state = {key:value for key, value in self.__dict__.items() if not key in nePasSvg}
- return (state)
-
- def __setstate__(self, state):
- self.__dict__ = state
- self.pion = PolygonePion()
- self.brillance = BrillancePion()
- self.etiquetteGraphique = None
- self._place = False
- super(Pion, self).__init__()
- def paint(self, painter, option, widget = None):
- """reimplemente de QGraphicsItem: on ne peint pas cet item, seulement ses items enfants"""
- pass
- def txtId(self):
- """renvoie le nom et le numero complementaire du pion"""
- txt = dmF.contractTxt(self._nom, 20)
- return "{} {}".format(txt, self.numComplementaire)
- def icone(self):
- """renvoie l'image a afficher dans les listes"""
- return self.logo
- def yReel(self):
- """renvoie le y reel (pour les contructions graphiques)"""
- if 1 == (self.position[0] % 2):
- y = self.position[1] + 0.5
- else:
- y = self.position[1]
- return y
-
- ###attributs du pion
- def position(self):
- """retourne la position actuelle du pion"""
- return self.position
- def projectionValide(self, proj):
- """prend en parametre la projection en cours, et renvoie vrai si celle-ci est valide
- attention: cette fonction peut etre appellee avant la creation du pion
- *** reimplemente dans les classes heritees ***"""
- lst = proj.listeCases()
- for case in lst:
- #1- la case est occupee par un autre combattant
- if (case.occupant() > 0): return False
- #2- z1 retourne None
- z1 = case.z1()
- if z1 == None: return False
- #3- la hauteur sous le plafond est insuffisante : (zP - z1) < h
- if case.zP() and not self.hMax:
- if case.zP() - z1 < self.h: return False
-
- return True
- def coordOccupees(self):
- """retourne la liste des cases occupees sur le plateau par le pion (x,y)"""
- retour = []
- if self.position != (-1, -1):
- for x, y in self.forme.listeCases(self.position, self.nbRotations):
- # for z in range(0, self.h):
- # retour.append((x, y, (self.plateau.cases[(x, y)].z0 + self.zR + z)))
- retour.append((x, y))
- return retour
- def espaceOccupe(self):
- """retourne l'espace occupe sur le plateau par le pion
- sous la forme de coordonnees (x, y, zA)"""
- retour = []
- if self.position != (-1, -1):
- for x, y in self.forme.listeCases(self.position, self.nbRotations):
- zA = self.plateau.cases[(x, y)].zA(self.numero)
- for z in range(0, (self.h if self.h > 0 else 1)):
- retour.append((x, y, (zA + z)))
- return retour
-
- def occuper(self):
- """signale aux cases qu'il occupe cet espace"""
- for x, y in self.coordOccupees():
- self.plateau.cases[(x,y)].occuper(self.numero, self.h)
-
- def liberer(self):
- """signale aux cases qu'il n'occupe plus cet espace"""
- for x, y in self.coordOccupees():
- self.plateau.cases[(x,y)].liberer(self.numero)
- def majZ(self, zR):
- """met a jour l'altitude relative zR du pion"""
- if zR != self.zR: self.zR = zR
- def zA(self):
- """retourne la coord z absolue du pion"""
- return (self.plateau.cases[self.position].zA(self.numero))
- ########### fonctions graphiques et geometriques ##############
- def ajouterAuPlateau(self, plateau):
- """cerre l'objet graphique representant le pion et le place sur le plateau"""
- self.plateau = plateau
- #definition de la forme (interpretation de formeDef)
- self.forme = Forme(self.plateau.formeCases)
- if len(self.formeDef[self.plateau.formeCases]) > 0:
- self.forme.definirForme(self.formeDef[self.plateau.formeCases])
- #parametres de l'objet graphique
- self.setFlag(QGraphicsItem.ItemHasNoContents)
- # self.setHandlesChildEvents(True)
- self.setFiltersChildEvents(True)
- self.setAcceptHoverEvents(True)
- ### ajout des items enfants (dans l'ordre d'empilement)
- #creation du polygone
- polygone = self.plateau.geo.polygoneAgglo(self.forme.listeCases((0,0)))
- self.pion.creer(self, polygone)
- #image:
- self.img.creer(self)
- #brillance (au survol)
- self.brillance.creer(self)
-
- #on ajoute l'objet au plateau
- self.plateau.addItem(self)
- self.setZValue(90)
- self.majPosition(self.position, self.nbRotations)
- self._place = True
-
- def majPosition(self, nouvellePosition, nbRotations = 0):
- """met a jour la position de l'objet graphique et de sa forme en fonction de sa position enregistree"""
- if self.plateau:
- #on met a jour l'occupation des cases
- if self._place: self.liberer()
- #on met a jour la position du pion
- self.position = nouvellePosition
- self.majNbRotation(nbRotations)
- #on replace
- if self.plateau.formeCases == "H":
- positionGraphique = QPointF(self.position[0] * 0.866 * 120, self.yReel() * 120)
- else:
- positionGraphique = QPointF(self.position[0] * 120, self.position[1] * 120)
- self.prepareGeometryChange()
- self.setPos(positionGraphique)
- self.pion.maj()
-
- #maj de l'image
- self.img.maj()
-
- #on met a jour l'occupation des cases
- self.occuper()
-
- #gestion de la pile du plateau
- self.posPile = self.plateau.incrementerPile()
- def majEtiquette(self):
- """met a jour la taille, le format et l'orientation de l'etiquette"""
- self.etiquetteGraphique = QGraphicsSimpleTextItem(QString().fromUtf8(self.txtId()))
- self.etiquetteGraphique.setPos(QPointF(self.etiquette.dx - 0.112*self.plateau.hCase, \
- self.etiquette.dy - 0.5*self.plateau.hCase))
- police = QFont("Verdana", self.etiquette.taille_police)
- police.setBold(self.etiquette.gras)
- self.etiquetteGraphique.setFont(police)
- self.etiquetteGraphique.setParentItem(self)
- self.etiquetteGraphique.setZValue(94)
-
- def majNbRotation(self, nbRotations):
- """ajoute/retranche le nombre au nombre total de rotations du pion"""
- self.nbRotations = nbRotations
- if self.plateau.formeCases == "H":
- rotationsTour = 6
- else:
- rotationsTour = 4
-
- if self.nbRotations >= 0:
- self.nbRotations = self.nbRotations % rotationsTour
- else:
- self.nbRotations = self.nbRotations % (-rotationsTour)
- def retirerDuPlateau(self):
- """'deconnecte' les items enfants avant de supprimer du pion du plateau"""
- self.liberer()
- self.setVisible(False)
- self.brillance.supprimer()
- self.plateau.removeItem(self.brillance)
- self.img.supprimer()
- self.plateau.removeItem(self.img)
- self.pion.supprimer()
- self.plateau.removeItem(self.pion)
- if self.etiquetteGraphique:
- self.etiquetteGraphique.prepareGeometryChange()
- self.etiquetteGraphique.setParentItem(None)
- self.plateau.removeItem(self.etiquetteGraphique)
- self.plateau.removeItem(self)
- self.plateau = None
- ###effets graphiques
- def afficheOmbreSelection(self, actif = False):
- """modifie l'ombre du pion en fonction de si celui-ci est selectionne ou non"""
- self.pion.ombre(actif)
-
- def surbrillance(self, active, opacite = 0.7, couleur = "white"):
- """active/desactive la surbrillance"""
- self.brillance.activer(active, opacite, self.couleurSurbrillance(couleur))
-
- def estCibleAttaque(self, estCible, possible = True):
- """le pion s'affiche comme etant cible d'une attaque"""
- if not possible:
- couleur = "red"
- else:
- couleur = "white"
- self.surbrillance(estCible, 0.8, couleur)
-
- def couleurSurbrillance(self, couleur = "white"):
- """renvoie une QColor visible pour la surbrillance, selon la couleur du pion"""
- retour = QColor(couleur)
- if self.pion.brush().color().lightness() > 220:
- retour = retour.darker(140)
- elif self.pion.brush().color().lightness() < 80:
- retour = retour.lighter(140)
- return retour
- ##################
- ############### evenements clavier et souris ##############
- def boundingRect(self):
- return QRectF()
-
- #######################
- class PolygonePion(QGraphicsPolygonItem):
- """polygone representant le pion"""
- def __init__(self):
- super(PolygonePion, self).__init__()
-
- def __setstate__(self, state):
- self.__dict__ = state
- super(PolygonePion, self).__init__()
-
- def numero(self):
- return self._pion.numero
-
- def creer(self, pion, polygone):
- self._pion = pion
- self.setPolygon(polygone)
- self.setAcceptHoverEvents(True)
- self.setFlag(QGraphicsItem.ItemIsFocusable) #l'item peut recevoir des commandes souris/clavier
- self.setPos(QPointF(0,0))
- origine = QPointF(2*0.2886*120, 0.5*120) if self._pion.plateau.formeCases == "H" else QPointF(0.5*120, 0.5*120)
- self.setTransformOriginPoint(origine)
-
- pinceau = QPen()
- pinceau.setWidth(10)
- couleur = self._pion.couleur if self._pion.couleur.isValid() else QColor(150, 150, 150)
- pinceau.setColor(couleur.darker(130))
- self.setPen(pinceau)
- self.setBrush(couleur)
-
- #ombre
- self.shadow = QGraphicsDropShadowEffect()
- self.shadow.setColor(QColor(50, 50, 50))
- self.shadow.setXOffset(1)
- self.shadow.setYOffset(2)
- self.shadow.setBlurRadius(3)
- self.shadow.setEnabled(True)
- self.setGraphicsEffect(self.shadow)
-
- self.setParentItem(self._pion)
-
- # self.setZValue(91)
- def maj(self):
- angleRotation = 60 if self._pion.plateau.formeCases == "H" else 90
- self.setRotation(self._pion.nbRotations * angleRotation)
- def ombre(self, actif):
- if actif:
- self.shadow.setXOffset(3)
- self.shadow.setYOffset(3)
- else:
- self.shadow.setXOffset(1)
- self.shadow.setYOffset(2)
-
- def supprimer(self):
- self.prepareGeometryChange()
- self.setParentItem(None)
- class BrillancePion(QGraphicsPolygonItem):
- """polygone representant le pion"""
- def __init__(self):
- super(BrillancePion, self).__init__()
- def __setstate__(self, state):
- self.__dict__ = state
- super(BrillancePion, self).__init__()
-
- def numero(self):
- return self._pion.numero
-
- def creer(self, pion):
- self._pion = pion
- self.setPolygon(self._pion.pion.polygon())
- self.setVisible(False)
- self.setFlag(QGraphicsItem.ItemIsFocusable)
- self.setAcceptHoverEvents(True)
- self.setParentItem(self._pion.pion)
- # self.setZValue(93)
- def activer(self, actif, opacite, couleur):
- if actif:
- self.setOpacity(opacite)
- self.setBrush(couleur)
- self.setPen(self._pion.pion.pen())
- self.setVisible(actif)
- def supprimer(self):
- self.prepareGeometryChange()
- self.setParentItem(None)
- class ImgPion(QGraphicsPixmapItem):
- def __init__(self):
- super(ImgPion, self).__init__()
- self._pion = None
- self.rimage = RImage() #ressource: image
- self.kx = 10 #coeff d'agrandissement horizontal
- self.ky = 10 #coeff d'agrandissement vertical
- self.dx = 0 #decalage horizontal
- self.dy = 0 #decalage vertical
- self.rotation = 0 #rotation(en degres)
- self.pivote = False #l'image pivote avec le pion?
- self.masqueAuto = False #creer un masque automatiquement
- def __setstate__(self, state):
- self.__dict__ = state
- super(ImgPion, self).__init__()
- def numero(self):
- num = self._pion.numero if self._pion else -1
- return num
-
- def creer(self, pion):
- self._pion = pion
- if not self.rimage.estValide(): return
- pix = self.rimage.pix()
- if pix.isNull(): return
-
- self.setAcceptedMouseButtons(Qt.NoButton)
- if pix.height() >= pix.width():
- pix = pix.scaledToHeight( 120 * 0.9, Qt.SmoothTransformation)
- else:
- pix = pix.scaledToWidth( 120 * 0.9, Qt.SmoothTransformation)
- pix = pix.scaled( (self.kx / 10) * pix.width(), \
- (self.ky / 10) * pix.height(), \
- Qt.IgnoreAspectRatio, Qt.SmoothTransformation )
- self.setPixmap(pix)
-
- if self.rimage.idR() == self._pion.logo.idR():
- #si l'image est le logo, elle ne doit pas pivoter
- self.setParentItem(self._pion)
- else:
- self.setParentItem(self._pion.pion)
- # self.setZValue(92)
-
- def maj(self):
- """met a jour la taille, la position et l'orientation de l'image"""
- pix = self.pixmap()
- if pix.isNull(): return
- deltaX = self.dx + 0.5 * ( (120 * 1.1544) - pix.width() )
- deltaY = self.dy + 0.5 * ( 120 - pix.height())
- self.setRotation(self.rotation)
- self.setPos(QPointF(deltaX, deltaY))
-
- def supprimer(self):
- self.prepareGeometryChange()
- self.setParentItem(None)
-
- class EtiquettePion():
- def __init__(self):
- self.txt = ""
- self.taille_police = 28 #taille de la police
- self.gras = False #en gras
- self.dx = 0 #decalage horizontal
- self.dy = 0 #decalage vertical
|