Actions.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #from __future__ import unicode_literals
  2. # -*- coding: utf-8 -*-
  3. from PyQt4.QtCore import *
  4. from PyQt4.QtGui import *
  5. import regles
  6. import AEtoile
  7. import time, threading
  8. class Action(object):
  9. """action effectuee par un combattant sur le plateau de jeu"""
  10. def __init__(self):
  11. self._num = None #no du pion actif
  12. self._coordCible = None #coord de la case ciblee par le curseur
  13. self._cible = None #cible (case ou pion)
  14. self._sourceCurseur = ""
  15. self._nomBouton = ""
  16. def activer(self, plateau, numPion):
  17. self.plateau = plateau
  18. self._num = numPion
  19. self.enfoncerBouton(True)
  20. self.activerCurseur()
  21. self.creerItemsGraphiques()
  22. def majCoordCible(self, coord):
  23. """met a jour les coordonnees de la cible,
  24. cad la case actuellement survolee par la souris"""
  25. if self.plateau.coordonneesValides(coord):
  26. self._coordCible = coord
  27. self.maj()
  28. def desactiver(self):
  29. self.afficherCibles(False)
  30. self.detruireItemsGraphiques()
  31. self.desactiverCurseur()
  32. self.enfoncerBouton(False)
  33. def valider(self):
  34. #envoyer signal
  35. self.envoiSignal()
  36. self.desactiver()
  37. def estValide(self):
  38. return True
  39. def maj(self):
  40. pass
  41. def acteur(self):
  42. return self.plateau.combattants[self._num]
  43. def coordActeur(self):
  44. return self.acteur().position
  45. def activerCurseur(self):
  46. if len(self._sourceCurseur) > 0:
  47. curseurPix = QPixmap(self._sourceCurseur)
  48. if not curseurPix.isNull():
  49. curseur = QCursor(curseurPix, 0, curseurPix.height())
  50. self.plateau.fenetre.ui.cbt_vue.setCursor(curseur)
  51. def desactiverCurseur(self):
  52. self.plateau.fenetre.ui.cbt_vue.setCursor(QCursor(Qt.ArrowCursor))
  53. def enfoncerBouton(self, actif):
  54. for bouton in self.plateau.fenetre.ui.pi_actions.findChildren(QToolButton):
  55. if bouton.objectName() == self._nomBouton:
  56. bouton.setChecked(actif)
  57. #manipulation des items graphiques
  58. def creerItemsGraphiques(self):
  59. pass
  60. def majItemsGraphiques(self):
  61. pass
  62. def detruireItemsGraphiques(self):
  63. pass
  64. #affichage des cibles
  65. def afficherCibles(self, actif):
  66. pass
  67. #envoi du signal en cas de validation
  68. def envoiSignal(self):
  69. pass
  70. def pivoter(self, modRotation):
  71. pass
  72. class Deplacement(Action):
  73. ### a completer avec des icones de deplacement,
  74. #la prise en compte de la nage et de l'escalade
  75. #et le calcul du cout de deplacement
  76. def __init__(self):
  77. super(Deplacement, self).__init__()
  78. self._chemin = [] #liste des coord des cases a traverser
  79. self._chercheurChemin = None
  80. self._cout = 0 #cout en points de dep
  81. self.cible_aConfirmer = None
  82. self._sourceCurseur = ""
  83. self._nomBouton = "pi_deplacement"
  84. def activer(self, plateau, numPion):
  85. super(Deplacement, self).activer(plateau, numPion)
  86. self.plateau.proj.creer(self.acteur())
  87. def desactiver(self):
  88. self.plateau.proj.desactiver()
  89. if self._chercheurChemin:
  90. self._chercheurChemin.arreter()
  91. self._chercheurChemin = None
  92. super(Deplacement, self).desactiver()
  93. def valider(self):
  94. if not self.cible_aConfirmer or self.cible_aConfirmer != self._coordCible:
  95. self.cible_aConfirmer = self._coordCible
  96. self.creerChemin()
  97. else:
  98. if self.estValide() and self.plateau.proj.projectionValide():
  99. self.acteur().majPosition(self.plateau.proj.coord(), self.plateau.proj.nbRotations())
  100. super(Deplacement, self).valider()
  101. def estValide(self):
  102. return len(self._chemin)>0
  103. def majCoordCible(self, coord):
  104. if coord != self.coordActeur():
  105. super(Deplacement, self).majCoordCible(coord)
  106. def maj(self):
  107. self.plateau.proj.majCoord(self._coordCible)
  108. def pivoter(self, modRotation):
  109. self.plateau.proj.majRotation(modRotation)
  110. def creerChemin(self):
  111. self.afficherCibles(False)
  112. self._chemin = []
  113. self._cout = 0
  114. if self._chercheurChemin:
  115. self._chercheurChemin.arreter()
  116. self._chercheurChemin = None
  117. self._chercheurChemin = AEtoile.Chemin(self.plateau, self.coordActeur(), self._coordCible)
  118. self._chemin = self._chercheurChemin.liste()
  119. self.afficherCibles(True)
  120. def afficherCibles(self, actif):
  121. for coord, cout in self._chemin:
  122. valide = True
  123. if actif:
  124. if cout > self.acteur().deplacement: valide = False
  125. else:
  126. cout = 0
  127. self.plateau.cases[coord].majAffichageDeplacement(cout, valide)
  128. def envoiSignal(self):
  129. cout = self._chemin[-1][1] #cout de deplacement retenu pour la derniere case
  130. print "{} s'est deplacé et a utilisé {} points de mouvement".format(self.acteur().txtId(), cout)
  131. class Attaque(Action):
  132. """attaque pre-parametree affectee a un pion, un personnage ou une creature"""
  133. def __init__(self):
  134. super(Attaque, self).__init__()
  135. self.nom = "Attaque"
  136. self.portee = 1 #portee max en cases
  137. self.attributs = regles.listeAttributsAttaques()
  138. self.notes = ""
  139. class Cac(Attaque):
  140. """attaque au corps a corps"""
  141. def __init__(self):
  142. super(Cac, self).__init__()
  143. self.nom = "Attaque au corps-à-corps"
  144. self._pionCible = None
  145. self._sourceCurseur = ""
  146. self._nomBouton = "pi_attaqueCac"
  147. def desactiver(self):
  148. self.afficherCibles(False)
  149. super(Cac, self).desactiver()
  150. def valider(self):
  151. if self.estValide() and self._pionCible:
  152. super(Cac, self).valider()
  153. def maj(self):
  154. self.afficherCibles(False)
  155. pionCible = self.plateau.cases[self._coordCible].pionOccupant()
  156. if pionCible != None and pionCible != self.plateau.pionSelectionne():
  157. self._pionCible = pionCible
  158. else:
  159. self._pionCible = None
  160. self.afficherCibles(True)
  161. def estValide(self):
  162. return (self._coordCible in self.plateau.zone(self.plateau.pionSelectionne().position, self.portee, 0, False, True))
  163. def afficherCibles(self, actif):
  164. if self._pionCible:
  165. self._pionCible.estCibleAttaque(actif, self.estValide())
  166. def envoiSignal(self):
  167. print "{} a attaqué {} au corps-à-corps".format(self.acteur().txtId(), self._pionCible.txtId())
  168. class Distance(Attaque):
  169. """attaque a distance"""
  170. def __init__(self):
  171. super(Distance, self).__init__()
  172. self.nom = "Attaque à distance"
  173. self._itemLigne = None
  174. self._pionCible = None
  175. self._sourceCurseur = ""
  176. self._nomBouton = "pi_attaqueDist"
  177. def majCoordCible(self, coord):
  178. if self._pionCible:
  179. self._pionCible.estCibleAttaque(False, self.estValide())
  180. if self._coordCible in self.plateau.cases:
  181. self.plateau.cases[self._coordCible].majEstCibleCurseur(False)
  182. super(Distance, self).majCoordCible(coord)
  183. def valider(self):
  184. if self.estValide() and self._pionCible != None:
  185. super(Distance, self).valider()
  186. def maj(self):
  187. """met a jour la ligne de mire representant l'attaque a distance"""
  188. self.afficherCibles(False)
  189. pionCible = self.plateau.cases[self._coordCible].pionOccupant()
  190. self.majItemsGraphiques()
  191. if pionCible != None and pionCible != self.plateau.pionSelectionne():
  192. self._pionCible = pionCible
  193. else:
  194. self._pionCible = None
  195. self.afficherCibles(True)
  196. def estValide(self):
  197. return self.plateau.estCibleAttaqueDistValide(self.plateau.pionSelectionne().position, self._coordCible, 0)
  198. def afficherCibles(self, actif):
  199. if self._pionCible:
  200. self._pionCible.estCibleAttaque(actif, self.estValide())
  201. else:
  202. #si pas de pion vise, on affiche la case cible comme visee
  203. self.plateau.cases[self._coordCible].majEstCibleCurseur(actif, self.estValide())
  204. def creerItemsGraphiques(self):
  205. self._itemLigne = QGraphicsLineItem()
  206. self._itemLigne.setZValue(100)
  207. pinceau = QPen()
  208. pinceau.setWidth(6)
  209. self._itemLigne.setPen(pinceau)
  210. self._itemLigne.prepareGeometryChange()
  211. self.plateau.addItem(self._itemLigne)
  212. def majItemsGraphiques(self):
  213. self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
  214. self.plateau.cases[self._coordCible].centreGraphique))
  215. def detruireItemsGraphiques(self):
  216. if self._itemLigne != None:
  217. self._itemLigne.prepareGeometryChange()
  218. self.plateau.removeItem(self._itemLigne)
  219. self._itemLigne = None
  220. def envoiSignal(self):
  221. print "{} a attaqué {} a distance".format(self.acteur().txtId(), self._pionCible.txtId())
  222. class Zone(Attaque):
  223. """attaque de zone de base"""
  224. def __init__(self):
  225. super(Zone, self).__init__()
  226. self.nom = "Attaque de zone"
  227. self._itemLigne = None
  228. self._itemCible = None
  229. self._casesCibles = []
  230. self._sourceCurseur = ""
  231. self._nomBouton = "pi_attaqueZone"
  232. def valider(self):
  233. if self.estValide() and len(self._casesCibles) > 0:
  234. super(Zone, self).valider()
  235. def desactiver(self):
  236. self.afficherCibles(False)
  237. self.detruireItemsGraphiques()
  238. def maj(self):
  239. """maj la forme de l'attaque de zone et les items cibles"""
  240. self.afficherCibles(False)
  241. self.majItemsGraphiques()
  242. self.majCibles()
  243. self.afficherCibles(True)
  244. def majCibles(self):
  245. """met a jour la liste des cases cibles"""
  246. self._casesCibles = []
  247. if self.estValide():
  248. for coord in self.plateau.casesSousForme(self._itemCible, True, True):
  249. if coord!= None and coord != self.plateau.pionSelectionne().position:
  250. self._casesCibles.append(coord)
  251. def afficherCibles(self, actif):
  252. for coord in self._casesCibles:
  253. self.plateau.cases[coord].majEstCibleCurseur(actif)
  254. for numCombattant in self.plateau.pionsSurListeCase(self._casesCibles):
  255. self.plateau.combattants[numCombattant].estCibleAttaque(actif)
  256. def creerItemsGraphiques(self):
  257. self._itemLigne = QGraphicsLineItem()
  258. self._itemLigne.setPen(QPen(QColor("black")))
  259. self._itemLigne.prepareGeometryChange()
  260. self.plateau.addItem(self._itemLigne)
  261. self._itemCible = QGraphicsEllipseItem()
  262. self._itemCible.setPen(QPen(QColor("black")))
  263. self._itemCible.prepareGeometryChange()
  264. self.plateau.addItem(self._itemCible)
  265. def detruireItemsGraphiques(self):
  266. if self._itemCible != None:
  267. self._itemCible.prepareGeometryChange()
  268. self.plateau.removeItem(self._itemCible)
  269. self._itemCible = None
  270. if self._itemLigne != None:
  271. self._itemLigne.prepareGeometryChange()
  272. self.plateau.removeItem(self._itemLigne)
  273. self._itemLigne = None
  274. def envoiSignal(self):
  275. touches = ""
  276. for num in self.plateau.pionsSurListeCase(self._casesCibles):
  277. touches += "{}, ".format(self.plateau.combattants[num].txtId())
  278. touches = touches[:-2]
  279. print "{} a lancé une attaque de zone. Les pions suivants sont touches: \n {}".format(self.acteur().txtId(), touches)
  280. class Ligne(Zone):
  281. """attaque de zone de forme lineaire"""
  282. def __init__(self):
  283. super(Ligne, self).__init__()
  284. self.nom = "Attaque de zone: ligne"
  285. def majItemsGraphiques(self):
  286. self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
  287. self.plateau.cases[self._coordCible].centreGraphique))
  288. def majCibles(self):
  289. """met a jour la liste des cases cibles"""
  290. self._casesCibles = []
  291. if self.estValide():
  292. for coord in self.plateau.casesSousForme(self._itemLigne, True, True):
  293. if coord!= None and coord != self.plateau.pionSelectionne().position:
  294. self._casesCibles.append(coord)
  295. def estValide(self):
  296. retour = True
  297. for coord in self._casesCibles:
  298. if self.plateau.cases[coord].estObstacleVision():
  299. retour = False
  300. break
  301. return retour
  302. class Disque(Zone):
  303. """attaque de zone de forme circulaire"""
  304. def __init__(self):
  305. super(Disque, self).__init__()
  306. self.nom = "Attaque de zone: disque"
  307. def majCoordCible(self, coord):
  308. if self._coordCible in self.plateau.cases:
  309. self.plateau.cases[self._coordCible].majEstCibleCurseur(False)
  310. super(Disque, self).majCoordCible(coord)
  311. def majCibles(self):
  312. self._casesCibles = self.plateau.zone(self._coordCible, self.rayon, 0)
  313. def afficherCibles(self, actif):
  314. super(Disque, self).afficherCibles(actif)
  315. #si on affiche une attaque invalide
  316. if not self.estValide():
  317. self.plateau.cases[self._coordCible].majEstCibleCurseur(actif, False)
  318. def majItemsGraphiques(self):
  319. self.rayon = self.plateau.fenetre.ui.pi_rayonAttaqueZone.value()
  320. self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
  321. self.plateau.cases[self._coordCible].centreGraphique))
  322. if self.estValide():
  323. rect = self.rectEllipseCirculaire(self.plateau.cases[self._coordCible].centreGraphique, self.rayon)
  324. if rect != None:
  325. self._itemCible.setRect(rect)
  326. self._itemCible.setVisible(self.estValide() and rect != None)
  327. def estValide(self):
  328. return self.plateau.estCibleAttaqueDistValide(self.plateau.pionSelectionne().position, self._coordCible)
  329. def rectEllipseCirculaire(self, centre, rayon):
  330. """renvoie le QRectF definissant une ellipse ayant le QPointF pour centre et le rayon en cases entres en param
  331. attention: l'ellipse n'est pas tout a fait circulaire, elle couvre horizontalement et
  332. verticalement le nombre de cases demandees"""
  333. rect = None
  334. if rayon > 0:
  335. p1 = QPointF((centre.x() - (rayon*self.plateau.hCase)), (centre.y() - (rayon*self.plateau.hCase)))
  336. p2 = QPointF((centre.x() + (rayon*self.plateau.hCase)), (centre.y() + (rayon*self.plateau.hCase)))
  337. if p1 != p2:
  338. rect = QRectF()
  339. rect.setTopLeft(p1)
  340. rect.setBottomRight(p2)
  341. return rect
  342. class Cone(Zone):
  343. """attaque de zone de forme conique"""
  344. def __init__(self):
  345. super(Cone, self).__init__()
  346. self.nom = "Attaque de zone: cône"
  347. def creerItemsGraphiques(self):
  348. self._itemCible = QGraphicsPolygonItem()
  349. self._itemCible.setPen(QPen(QColor("black")))
  350. self._itemCible.prepareGeometryChange()
  351. self.plateau.addItem(self._itemCible)
  352. def majItemsGraphiques(self):
  353. self._itemCible.setPolygon(self.polygoneCone(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
  354. self.plateau.cases[self._coordCible].centreGraphique))
  355. def polygoneCone(self, point1, point2):
  356. """renvoie le polygone du cone defini par les deux points (origine, distance)"""
  357. ligne1 = QLineF(point1, point2)
  358. longueur = ligne1.length()
  359. ligne1.setAngle(ligne1.angle() + 22.5)
  360. ligne1.setLength(1.1547*longueur)
  361. ligne2 = QLineF(point1, point2)
  362. ligne2.setAngle(ligne2.angle() - 22.5)
  363. ligne2.setLength(1.1547*longueur)
  364. polygone = QPolygonF()
  365. polygone.append(point1)
  366. polygone.append(ligne1.p2())
  367. polygone.append(ligne2.p2())
  368. return polygone