Pion.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. #from __future__ import unicode_literals
  2. # -*- coding: utf-8 -*-
  3. from __future__ import division
  4. import os
  5. from PyQt4.QtCore import *
  6. from PyQt4.QtGui import *
  7. from Creature import Creature
  8. from Forme import Forme
  9. class Pion(QGraphicsItem):
  10. """pion du plateau de combat"""
  11. def __init__(self, plateau, numero, parent=None):
  12. super(Pion, self).__init__()
  13. #plateau
  14. self.plateau = plateau
  15. #attributs
  16. self.numero = numero
  17. self.creature = Creature()
  18. self.nom = "pion"
  19. self.numTxt = ""
  20. self.lstCarac = self.creature.listeCarac() #caracteristiques par defaut issues des regles utilisees
  21. self.position = (-1, -1)
  22. self.forme = Forme(self.plateau.formeCases)
  23. self.champDeplacement = {} #dictionnaire des cases jusqu'auquelles le pion peut se deplacer (coordonnees : distance)
  24. self.deplacementRestant = 0
  25. self.z = 0 #hauteur z par rapport a l'altitude de la case occupee
  26. self.hauteur = 1 #nombre de cases occupees en hauteur
  27. self.pixGraphique = None
  28. self.largeurOriginale = 0
  29. self.hauteurOriginale = 0
  30. self.polygoneGraphique = None
  31. self.nbRotations = 0
  32. self.etat = ""
  33. self.attaques = [] #liste des attaques pre-parametrees du pion
  34. self.notes = ""
  35. def __getstate__(self):
  36. self.idCreature = self.creature.id
  37. state = {key:value for key, value in self.__dict__.items() if not key in ["plateau", "brillance", "shadow", "creature", \
  38. "polygonesForme", "pixGraphique"]}
  39. return (state)
  40. def __setstate__(self, state):
  41. self.__dict__ = state
  42. self.creature = charger("librairie\\creature", self.idCreature)
  43. ## if self.creature == None: ##############
  44. def creer(self, posX, posY, nom, numTxt = "", creature = None, couleur = None, nbRotations = 0):
  45. """place le pion sur le plateau"""
  46. #definition de la forme
  47. if creature != None:
  48. self.creature = creature
  49. if len(self.creature.formeDef[self.plateau.formeCases]) > 0:
  50. self.forme.definirForme(self.creature.formeDef[self.plateau.formeCases])
  51. self.attaques = creature.listeAttaques()
  52. self.lstCarac = self.creature.listeCarac()
  53. else:
  54. if couleur != None:
  55. if couleur.isValid():
  56. self.creature.couleur = couleur
  57. self.creerPolygone()
  58. self.deplacementRestant = self.creature.deplacement
  59. self.nom = nom
  60. self.numTxt = numTxt
  61. #aspect graphique
  62. self.majAspectGraphique()
  63. #ajout de l'objet graphique
  64. self.plateau.addItem(self)
  65. #enregistrement et maj de la position
  66. self.majPosition((posX, posY), nbRotations)
  67. def recreer(self, plateau):
  68. """recree le pion (apres un chargement du plateau par ex)"""
  69. self.plateau = plateau
  70. super(Pion, self).__init__()
  71. self.pixGraphique = None
  72. self.polygoneGraphique = None
  73. self.creerPolygone()
  74. self.majAspectGraphique()
  75. self.plateau.addItem(self)
  76. self.majPosition(self.position, self.nbRotations)
  77. def creerPolygone(self):
  78. """cree le polygone representant le pion (doit etre cree avant toute rotation)"""
  79. polygone = self.plateau.polygoneAgglo(self.forme.listeCases((0,0)))
  80. self.polygoneGraphique = QGraphicsPolygonItem()
  81. self.polygoneGraphique.setPolygon(polygone)
  82. self.polygoneGraphique.setAcceptHoverEvents(True)
  83. self.polygoneGraphique.setFlag(QGraphicsItem.ItemIsFocusable) #l'item peut recevoir des commandes souris/clavier
  84. self.polygoneGraphique.setParentItem(self)
  85. self.polygoneGraphique.setPos(QPointF(0,0))
  86. if self.plateau.formeCases == "H":
  87. self.polygoneGraphique.setTransformOriginPoint(QPointF(2*0.2886*self.plateau.hCase,0.5*self.plateau.hCase))
  88. else:
  89. self.polygoneGraphique.setTransformOriginPoint(QPointF(0.5*self.plateau.hCase,0.5*self.plateau.hCase))
  90. def majAspectGraphique(self):
  91. """met a jour l'aspect graphique du pion
  92. (seulement les parametres independants de la position et de la forme de l'objet)"""
  93. self.setZValue(10)
  94. #interactions graphiques
  95. ## self.setFlag(QGraphicsItem.ItemIsFocusable) #l'item peut recevoir des commandes souris/clavier
  96. self.setFlag(QGraphicsItem.ItemHasNoContents) #inutile de peindre l'item
  97. ## self.setAcceptHoverEvents(True) #accepte les evenements survol souris
  98. self.setHandlesChildEvents(True)
  99. #classes graphiques
  100. self.brillance = QGraphicsColorizeEffect()
  101. self.shadow = QGraphicsDropShadowEffect()
  102. #etiquette
  103. self.text = QGraphicsSimpleTextItem("{}. {}".format(self.numero, self.txtId()))
  104. self.text.setPos(QPointF(self.creature.txt["dx"]-0.112*self.plateau.hCase, self.creature.txt["dy"]-0.275*self.plateau.hCase))
  105. police = QFont("Georgia", self.creature.txt["taille_police"])
  106. police.setBold(self.creature.txt["gras"])
  107. self.text.setFont(police)
  108. self.text.setRotation(self.creature.txt["rotation"])
  109. self.text.setParentItem(self)
  110. #bordure
  111. pinceau = QPen()
  112. pinceau.setColor(self.creature.couleur.darker(130))
  113. pinceau.setWidth(10)
  114. self.polygoneGraphique.setPen(pinceau)
  115. #couleur de fond
  116. if self.creature.couleur.isValid():
  117. self.polygoneGraphique.setBrush(self.creature.couleur)
  118. else:
  119. self.polygoneGraphique.setBrush(QColor(255, 0, 0, 150))
  120. #ombre
  121. self.shadow.setColor(QColor(50, 50, 50))
  122. self.shadow.setXOffset(1)
  123. self.shadow.setYOffset(2)
  124. self.shadow.setBlurRadius(3)
  125. self.shadow.setEnabled(True)
  126. self.polygoneGraphique.setGraphicsEffect(self.shadow)
  127. #brillance (invisible par defaut, est un polygone semi transparent superpose au pion)
  128. self.polygoneBrillance = QGraphicsPolygonItem()
  129. self.polygoneBrillance.setPolygon(self.polygoneGraphique.polygon())
  130. self.polygoneBrillance.setVisible(False)
  131. self.polygoneGraphique.setFlag(QGraphicsItem.ItemIsFocusable)
  132. self.setAcceptHoverEvents(True) #accepte les evenements survol souris
  133. self.polygoneBrillance.setParentItem(self.polygoneGraphique)
  134. def majPosition(self, nouvellePosition, nbRotations = 0):
  135. """met a jour la position de l'objet graphique et de sa forme en fonction de sa position enregistree"""
  136. valide = True
  137. if nouvellePosition in self.champDeplacement and self.plateau.modePrincipal == "combat":
  138. distanceParcourue = self.champDeplacement[nouvellePosition]
  139. if distanceParcourue <= self.deplacementRestant:
  140. self.deplacementRestant -= distanceParcourue
  141. else:
  142. print ("pas assez de points de deplacement")
  143. valide = False
  144. if valide:
  145. #on met a jour l'occupation des cases
  146. if self.position != (-1,-1):
  147. for coord in self.forme.listeCases((self.position[0],self.position[1]), self.nbRotations):
  148. self.plateau.cases[coord].majOccupation(self)
  149. #on met a jour la position du pion
  150. self.position = nouvellePosition
  151. self.majNbRotation(nbRotations)
  152. #on replace
  153. if self.plateau.formeCases == "H":
  154. angleRotation = 60
  155. positionGraphique = QPointF(self.position[0] * 0.866 * self.plateau.hCase, self.position[1] * self.plateau.hCase)
  156. else:
  157. angleRotation = 90
  158. positionGraphique = QPointF(self.position[0] * self.plateau.hCase, self.position[1] * self.plateau.hCase)
  159. self.prepareGeometryChange()
  160. self.setPos(positionGraphique)
  161. self.polygoneGraphique.setRotation(self.nbRotations*angleRotation)
  162. #maj de l'image
  163. self.majImage()
  164. #on met a jour l'occupation des cases
  165. for coord in self.forme.listeCases((self.position[0],self.position[1]), self.nbRotations):
  166. self.plateau.cases[coord].majOccupation(self, self.z)
  167. def majImage(self):
  168. """met a jour la taille, la position et l'orientation de l'image"""
  169. if len(self.creature.img["nom"]) > 0:
  170. pix = QPixmap(QString.fromUtf8("img\\"+self.creature.img["nom"]))
  171. if self.creature.img["masqueAuto"]:
  172. pix.setMask(pix.createHeuristicMask())
  173. if not pix.isNull():
  174. if not self.pixGraphique:
  175. self.pixGraphique = QGraphicsPixmapItem()
  176. self.pixGraphique.setZValue(10)
  177. if pix.height() >= pix.width():
  178. pix = pix.scaledToHeight(self.plateau.hCase*0.9, Qt.SmoothTransformation)
  179. else:
  180. pix = pix.scaledToWidth(self.plateau.hCase*0.9, Qt.SmoothTransformation)
  181. self.largeurOriginale = pix.width()
  182. self.hauteurOriginale = pix.height()
  183. pix = pix.scaled((self.creature.img["kx"]/10)*pix.width(), \
  184. (self.creature.img["ky"]/10)*pix.height(), \
  185. Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
  186. self.pixGraphique.setPixmap(pix)
  187. deltaX = self.creature.img["dx"] + 0.5*(self.plateau.hCase*1.1544 - self.largeurOriginale)
  188. deltaY = self.creature.img["dy"] + 0.5*(self.plateau.hCase - self.hauteurOriginale)
  189. if self.creature.img["nom"] != self.creature.logo and self.creature.img["pivote"] == True:
  190. self.pixGraphique.setParentItem(self.polygoneGraphique)
  191. else:
  192. self.pixGraphique.setParentItem(self)
  193. self.pixGraphique.setRotation(self.creature.img["rotation"])
  194. self.pixGraphique.setPos(QPointF(deltaX, deltaY))
  195. def majNbRotation(self, nbRotations):
  196. self.nbRotations = nbRotations
  197. if self.plateau.formeCases == "H":
  198. rotationsTour = 6
  199. else:
  200. rotationsTour = 4
  201. if self.nbRotations >= 0:
  202. self.nbRotations = self.nbRotations % rotationsTour
  203. else:
  204. self.nbRotations = self.nbRotations % (-rotationsTour)
  205. def afficheOmbreSelection(self, actif = False):
  206. if actif:
  207. self.shadow.setXOffset(3)
  208. self.shadow.setYOffset(3)
  209. else:
  210. self.shadow.setXOffset(1)
  211. self.shadow.setYOffset(2)
  212. def surbrillance(self, active, opacite = 0.7, couleur = "white"):
  213. """active/desactive la surbrillance"""
  214. if active:
  215. self.polygoneBrillance.setOpacity(opacite)
  216. couleur = self.couleurSurbrillance(couleur)
  217. pinceau = self.polygoneGraphique.pen()
  218. self.polygoneBrillance.setBrush(couleur)
  219. self.polygoneBrillance.setPen(pinceau)
  220. self.polygoneBrillance.setVisible(active)
  221. def estCibleAttaque(self, estCible, possible = True):
  222. """le pion s'affiche comme etant cible d'une attaque"""
  223. if not possible:
  224. couleur = "red"
  225. else:
  226. couleur = "white"
  227. self.surbrillance(estCible, 0.8, couleur)
  228. def majZ(self, valeur):
  229. """met a jour l'altitude Z du pion"""
  230. if valeur != self.z:
  231. self.z = valeur
  232. def couleurSurbrillance(self, couleur = "white"):
  233. """renvoie une QColor visible pour la surbrillance, selon la couleur du pion"""
  234. retour = QColor(couleur)
  235. if self.polygoneGraphique.brush().color().lightness() > 220:
  236. retour = retour.darker(140)
  237. elif self.polygoneGraphique.brush().color().lightness() < 80:
  238. retour = retour.lighter(140)
  239. return retour
  240. def supprimer(self):
  241. """'deconnecte' les items enfants avant de supprimer du pion"""
  242. self.polygoneBrillance.prepareGeometryChange()
  243. self.polygoneBrillance.setParentItem(None)
  244. if self.pixGraphique != None:
  245. self.pixGraphique.prepareGeometryChange()
  246. self.pixGraphique.setParentItem(None)
  247. self.polygoneGraphique.prepareGeometryChange()
  248. self.polygoneGraphique.setParentItem(None)
  249. self.text.prepareGeometryChange()
  250. self.text.setParentItem(None)
  251. self.plateau.removeItem(self)
  252. self.plateau = None
  253. def boundingRect(self):
  254. return QRectF()
  255. def hoverEnterEvent(self, event):
  256. """evenement lors du survol de la souris (en entree)"""
  257. #super(Pion, self).hoverEnterEvent(event)
  258. if (self.plateau.pionSelectionne() == None and self.plateau.modeActif == "standard") or self.plateau.modeActif == "pionSupprimer":
  259. self.surbrillance(True, 0.4)
  260. self.plateau.pionSurvol(self.numero)
  261. event.ignore()
  262. def hoverLeaveEvent(self, event):
  263. """evenement lors du survol de la souris (en entree)"""
  264. #super(Pion, self).hoverLeaveEvent(event)
  265. self.surbrillance(False)
  266. self.plateau.pionSurvol(None)
  267. def mousePressEvent(self, event):
  268. """evenement lors du clic souris"""
  269. super(Pion, self).mousePressEvent(event)
  270. if event.button() == 1: #sur clic gauche
  271. accepte = self.plateau.pionClique(self.numero)
  272. if accepte: event.accept()
  273. else:
  274. event.ignore()
  275. def mouseDoubleClickEvent(self, event):
  276. """evenement lors du clic souris"""
  277. super(Pion, self).mouseDoubleClickEvent(event)
  278. if event.button() == 1: #sur clic gauche
  279. accepte = self.plateau.pionDoubleClic(self.numero)
  280. if accepte: event.accept()
  281. else:
  282. event.ignore()
  283. ##################
  284. ############### evenements clavier et souris ##############
  285. #*EP
  286. def boundingRect(self):
  287. return QRectF()
  288. def hoverEnterEvent(self, event):
  289. """evenement lors du survol de la souris (en entree)"""
  290. #super(Pion, self).hoverEnterEvent(event)
  291. if (self.plateau.pionSelectionne() == None and self.plateau.modeActif == "standard") or self.plateau.modeActif == "pionSupprimer":
  292. self.surbrillance(True, 0.4)
  293. self.plateau.pionSurvol(self.numero)
  294. event.ignore()
  295. def hoverLeaveEvent(self, event):
  296. """evenement lors du survol de la souris (en entree)"""
  297. #super(Pion, self).hoverLeaveEvent(event)
  298. self.surbrillance(False)
  299. self.plateau.pionSurvol(None)
  300. def mousePressEvent(self, event):
  301. """evenement lors du clic souris"""
  302. super(Pion, self).mousePressEvent(event)
  303. if event.button() == 1: #sur clic gauche
  304. accepte = self.plateau.pionClique(self.numero)
  305. if accepte: event.accept()
  306. else:
  307. event.ignore()
  308. def mouseDoubleClickEvent(self, event):
  309. """evenement lors du clic souris"""
  310. super(Pion, self).mouseDoubleClickEvent(event)
  311. if event.button() == 1: #sur clic gauche
  312. accepte = self.plateau.pionDoubleClic(self.numero)
  313. if accepte: event.accept()
  314. else:
  315. event.ignore()