#from __future__ import unicode_literals # -*- coding: utf-8 -*- import sys from PyQt4.QtCore import Qt, QLineF, QPointF, QRectF, SIGNAL from PyQt4.QtGui import QPixmap, QCursor, QToolButton, QIcon, QGraphicsLineItem, \ QPen, QColor, QGraphicsEllipseItem, QGraphicsPolygonItem, QPolygonF, QDialog, \ QApplication import AEtoile from lib.dialogues import dmVol from lib.ui.ecran_attaqueZone import Ui_zone_fenetre import regles class Action(object): """action effectuee par un combattant sur le plateau de jeu""" def __init__(self): self.plateau = None self._num = None #no du pion actif self._nom = "Action" self._coordCible = None #coord de la case ciblee par le curseur self._cible = None #cible (case ou pion) self._sourceCurseur = "" self._nomBouton = "" self._enCours = False self._desactivationDemandee = False #decorateur def autorise(f): def _autorise(self, *args): def fVide(*args): pass retour = fVide if not self._desactivationDemandee: retour = f retour(self, *args) return _autorise def nom(self): return self._nom def typeAtt(self): return "" def description(self): """description de l'action en cours, pour le panneau d'action""" return "" def activer(self, plateau, numPion): self.plateau = plateau self._num = numPion self.activerCurseur() self.creerItemsGraphiques() self._enCours = True self.majPanneauAction() def majCoordCible(self, coord): """met a jour les coordonnees de la cible, cad la case actuellement survolee par la souris""" if self.plateau.geo.coordonneesValides(coord): self._coordCible = coord self.maj() def desactiver(self): self._desactivationDemandee = True self.afficherCibles(False) self.detruireItemsGraphiques() self.desactiverCurseur() self._enCours = False self.majPanneauAction() def enCours(self): return self._enCours def valider(self): #envoyer signal self.envoiSignal() self.desactiver() def estValide(self): return True def maj(self): pass def acteur(self): return self.plateau.pions[self._num] def coordActeur(self): return self.acteur().position def activerCurseur(self): if len(self._sourceCurseur) > 0: curseurPix = QPixmap(self._sourceCurseur) if not curseurPix.isNull(): curseur = QCursor(curseurPix, 0, curseurPix.height()) self.plateau.fenetre.ui.cbt_vue.setCursor(curseur) def desactiverCurseur(self): self.plateau.fenetre.ui.cbt_vue.setCursor(QCursor(Qt.ArrowCursor)) def majPanneauAction(self): for bouton in self.plateau.fenetre.ui.page_act.findChildren(QToolButton): if self._enCours: if bouton.objectName() == self._nomBouton: bouton.setChecked(True) else: bouton.setVisible(False) else: bouton.setVisible(True) bouton.setChecked(False) self.plateau.fenetre.ui.act_description.setVisible(self._enCours) self.plateau.fenetre.ui.act_valider.setVisible(self._enCours) if self._enCours: self.plateau.fenetre.ui.act_description.majTexte(self.description()) #manipulation des items graphiques def creerItemsGraphiques(self): pass def majItemsGraphiques(self): pass def detruireItemsGraphiques(self): pass #affichage des cibles def afficherCibles(self, actif): pass #envoi du signal en cas de validation def envoiSignal(self): pass def pivoter(self, modRotation): pass class Deplacement(Action): ### a completer avec des icones de deplacement, #la prise en compte de la nage et de l'escalade #et le calcul du cout de deplacement def __init__(self): super(Deplacement, self).__init__() self._nom = "Deplacement" self._chemin = [] #liste des coord des cases a traverser self._chercheurChemin = None self._cout = 0 #cout en points de dep self._zCible = 0 self.cible_aConfirmer = None self._sourceCurseur = "" self._nomBouton = "act_deplacement" def typeAtt(self): return "dep" def description(self): txt = " - {} - \n".format(self.nom().upper()) if not self.cible_aConfirmer: txt += " Cliquez sur la case de destination\n" txt += " (Maintenez [MAJ] pour changer l'altitude cible)\n" txt += " (Maintenez [MAJ] pour changer l'altitude cible)\n" txt += " (Maintenez [MAJ] pour changer l'altitude cible)\n" else: txt += " Destination: {}\n".format(self.plateau.proj.coord()) if self._zCible != 0: txt += " Altitude de destination: {}\n".format(self._zCible) txt += self.decrireChemin() return txt def activer(self, plateau, numPion): super(Deplacement, self).activer(plateau, numPion) self.plateau.proj.creer(self.acteur()) def desactiver(self): self.plateau.proj.desactiver() if self._chercheurChemin: self._chercheurChemin.arreter() self._chercheurChemin = None super(Deplacement, self).desactiver() def valider(self): if not self.cible_aConfirmer or self.cible_aConfirmer != self._coordCible: self.cible_aConfirmer = self._coordCible self.recupZCible() self.creerChemin() else: if self.estValide() and self.plateau.proj.valide(): self.acteur().majPosition(self.plateau.proj.coord(), self.plateau.proj.nbRotations()) self.acteur().majZ(self._zCible) super(Deplacement, self).valider() def estValide(self): return len(self._chemin) > 0 def majCoordCible(self, coord): if coord != self.coordActeur(): super(Deplacement, self).majCoordCible(coord) def maj(self): self.plateau.proj.majCoord(self._coordCible) def pivoter(self, modRotation): self.plateau.proj.majRotation(modRotation) def creerChemin(self): self.afficherCibles(False) self._chemin = [] self._cout = 0 if self._chercheurChemin: self._chercheurChemin.arreter() self._chercheurChemin = None if self.plateau.cases[self._coordCible].terrain.franchissable and not (self.plateau.cases[self._coordCible].occupant() > 0): self._chercheurChemin = AEtoile.Chemin(self.plateau, self.coordActeur(), self._coordCible, self.acteur().zR, self._zCible) self._chemin = self._chercheurChemin.liste() self.afficherCibles(True) def decrireChemin(self): """decrit le chemin emprunte""" return "" def afficherCibles(self, actif): compte = 0 ; coutTotal = 0 ; valide = True z = self.plateau.cases[self.acteur().position].z1() for coord, cout in self._chemin: compte += 1 ; coutTotal += cout if actif: if int(coutTotal) > self.acteur().pM(): valide = False dz = self.plateau.cases[coord].z1() - z self.plateau.cases[coord].majAffichageDeplacement(compte, valide, dz) else: self.plateau.cases[coord].majAffichageDeplacement(0) z = self.plateau.cases[coord].z1() def envoiSignal(self): coutTotal = 0 for coord, cout in self._chemin: coutTotal += cout print "{} s'est deplacé et a utilisé {} points de mouvement".format(self.acteur().txtId(), cout) def recupZCible(self): self._zCible = self.acteur().zR class Vol(Deplacement): """idem que Deplacement, mais affiche en plus la boite de dialogue Vol (et n'utilise pas le meme algo de deplacement?)""" def __init__(self): super(Vol, self).__init__() self._nom = "Deplacement (vol)" self._zCible = 0 self._nomBouton = "pi_vol" def description(self): txt = " - {} - \n".format(self.nom().upper()) return txt def typeAtt(self): return "vol" def recupZCible(self): maxi = ((self.plateau.zP - self.acteur().h) if self.plateau.zP else None) nouveauZ = dmVol(self.acteur().zR, maxi) self._zCible = nouveauZ class Attaque(Action): """attaque pre-parametree affectee a un pion, un personnage ou une creature""" def __init__(self): super(Attaque, self).__init__() self._nom = "Attaque" self._icone = "" self._portee = 1 #portee max en cases self._rayon = 0 self._pionCible = None self._attributs = regles.listeAttributsAttaques() self._notes = "" def typeAtt(self): return "att" def description(self): return "" def nom(self): return self._nom def majNom(self, nom): if len(str(nom)) > 0: self._nom = nom def portee(self): return self._portee def majPortee(self, portee): try: ent = int(portee) if ent > 0: if ent >= 1000: ent = 999 self._portee = ent except: pass def rayon(self): return self._rayon def modifierRayon(self, val): if self._rayon < 30: rayon = self._rayon + val self.majRayon(rayon) def majRayon(self, rayon): try: ent = int(rayon) if ent > 0: if ent >= 100: ent = 99 self._rayon = ent except: pass def majAttribut(self, nom, nouvelleVal): if nom in self._attributs: if regles.attributAttaque(nom).controler(nouvelleVal): self._attributs[nom] = nouvelleVal def majAttributs(self, dicoAttributs): self._attributs = dicoAttributs def attributs(self): return self._attributs def notes(self): return self._notes def majNotes(self, notes): #on limite a 400 le nombre de caracteres notes = str(notes) self._notes = notes[0:400] def icone(self): return QIcon(self._icone) def ldmValide(self): x0, y0 = self.acteur().position z0 = self.acteur().zA() + self.acteur().h origine = (x0,y0,z0) #on essaie de cibler toutes les cases de la hauteur de la cible si c'est un pion cibles = [] x1, y1 = self._coordCible if self._pionCible: for zA in self.plateau.cases[self._coordCible].hOcc(self._pionCible.numero): cibles.append( (x1, y1, zA) ) else: cibles = [(x1, y1, self.plateau.cases[self._coordCible].z0)] for cible in cibles: if self.plateau.ldmValide(origine, cible): return True return False class Cac(Attaque): """attaque au corps a corps""" def __init__(self): super(Cac, self).__init__() self._nom = "Attaque au corps-à-corps" self._sourceCurseur = "" self._nomBouton = "act_attaqueCac" self._icone = "img\\curseurEpee.png" def typeAtt(self): return "cac" def desactiver(self): self.afficherCibles(False) super(Cac, self).desactiver() def description(self): txt = " - {} - \n".format(self.nom().upper()) txt += " Cliquez sur une cible a portee" return txt def valider(self): if self.estValide() and self._pionCible: super(Cac, self).valider() def maj(self): self.afficherCibles(False) pionCible = self.plateau.pions[self.plateau.cases[self._coordCible].occupant()] if pionCible != None and pionCible != self.plateau.pionSelectionne(): self._pionCible = pionCible else: self._pionCible = None self.afficherCibles(True) def estValide(self): return (self._coordCible in self.plateau.geo.zone(self.plateau.pionSelectionne().position, self.portee, 0, False, True)) def afficherCibles(self, actif): if self._pionCible: self._pionCible.estCibleAttaque(actif, self.estValide()) def envoiSignal(self): print "{} a attaqué {} au corps-à-corps".format(self.acteur().txtId(), self._pionCible.txtId()) class Distance(Attaque): """attaque a distance""" def __init__(self): super(Distance, self).__init__() self._nom = "Attaque à distance" self._itemLigne = None self._sourceCurseur = "" self._nomBouton = "act_attaqueDist" self._icone = ":/interface/16/ressource/arc_16.png" def typeAtt(self): return "dist" def description(self): txt = " - {} - \n".format(self.nom().upper()) txt += " Cliquez sur une cible visible" return txt def majCoordCible(self, coord): if self._pionCible: self._pionCible.estCibleAttaque(False, self.estValide()) if self._coordCible in self.plateau.cases: self.plateau.cases[self._coordCible].majEstCibleCurseur(False) super(Distance, self).majCoordCible(coord) def valider(self): if self.estValide() and self._pionCible != None: super(Distance, self).valider() def maj(self): """met a jour la ligne de mire representant l'attaque a distance""" self.afficherCibles(False) numCible = self.plateau.cases[self._coordCible].occupant() pionCible = self.plateau.pions[numCible] self.majItemsGraphiques() if pionCible != None and pionCible != self.plateau.pionSelectionne(): self._pionCible = pionCible else: self._pionCible = None self.afficherCibles(True) def estValide(self): return self.ldmValide() def afficherCibles(self, actif): valide = self.estValide() if actif else True if self._pionCible: self._pionCible.estCibleAttaque(actif, valide) else: #si pas de pion vise, on affiche la case cible comme visee self.plateau.cases[self._coordCible].majEstCibleCurseur(actif, valide) def creerItemsGraphiques(self): self._itemLigne = itemLdm() self._itemLigne.prepareGeometryChange() self.plateau.addItem(self._itemLigne) def majItemsGraphiques(self): self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \ self.plateau.cases[self._coordCible].centreGraphique)) def detruireItemsGraphiques(self): if self._itemLigne != None: self._itemLigne.prepareGeometryChange() self.plateau.removeItem(self._itemLigne) self._itemLigne = None def envoiSignal(self): print "{} a attaqué {} a distance".format(self.acteur().txtId(), self._pionCible.txtId()) class Zone(Attaque): """attaque de zone de base""" def __init__(self): super(Zone, self).__init__() self._nom = "Attaque de zone" self._itemLigne = None self._itemCible = None self._casesCibles = [] self._sourceCurseur = "" self._nomBouton = "act_attaqueZone" self._icone = ":/interface/16/ressource/baguette_16.png" def typeAtt(self): return "zone" def valider(self): if self.estValide() and len(self._casesCibles) > 0: super(Zone, self).valider() # def desactiver(self): # self.detruireItemsGraphiques() # super(Zone, self).desactiver() def description(self): txt = " - {} - \n".format(self.nom().upper()) txt += " Cliquez sur une cible visible" return txt def maj(self): """maj la forme de l'attaque de zone et les items cibles""" self.afficherCibles(False) self.majItemsGraphiques() self.majCibles() self.afficherCibles(True) def afficherCibles(self, actif): for coord in self._casesCibles: x = coord[0] ; y = coord[1] zA = 0 if len(coord) == 2 else coord[2] occupant = self.plateau.cases[(x, y)].occupant(zA) if occupant: pion = self.plateau.pions[occupant] pion.estCibleAttaque(actif) else: self.plateau.cases[(x, y)].majEstCibleCurseur(actif) def listePionsCibles(self): retour = [] for coord in self._casesCibles: z = 0 if len(coord) == 2 else coord[2] if self.plateau.cases[(coord[0], coord[1])].estOccupee(z): pion = self.plateau.cases[(coord[0], coord[1])].occupant(z) if not pion in retour: retour.append(pion) return retour def creerItemsGraphiques(self): self._itemLigne = QGraphicsLineItem() self._itemLigne.setPen(QPen(QColor("black"))) self._itemLigne.prepareGeometryChange() self.plateau.addItem(self._itemLigne) self._itemCible = QGraphicsEllipseItem() self._itemCible.setPen(QPen(QColor("black"))) self._itemCible.prepareGeometryChange() self.plateau.addItem(self._itemCible) def detruireItemsGraphiques(self): if self._itemCible != None: self._itemCible.prepareGeometryChange() self.plateau.removeItem(self._itemCible) self._itemCible = None if self._itemLigne != None: self._itemLigne.prepareGeometryChange() self.plateau.removeItem(self._itemLigne) self._itemLigne = None def envoiSignal(self): touches = "" for pion in self.listePionsCibles(): touches += "{}, ".format(pion.txtId()) touches = touches[:-2] print "{} a lancé une attaque de zone. Les pions suivants sont touches: \n {}".format(self.acteur().txtId(), touches) class Ligne(Zone): """attaque de zone de forme lineaire""" def __init__(self): super(Ligne, self).__init__() self._nom = "Attaque de zone: ligne" def typeAttZone(self): return "ligne" def majItemsGraphiques(self): if not self._desactivationDemandee: self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \ self.plateau.cases[self._coordCible].centreGraphique)) def majCibles(self): """met a jour la liste des cases cibles""" if not self._desactivationDemandee: self._casesCibles = [] x1, y1 = self.acteur().position z1 = self.acteur().zA() + self.acteur().h x2, y2 = self._coordCible occupant = self.plateau.cases[(x2, y2)].occupant() z2 = self.plateau.cases[(x2, y2)].zA(occupant) if occupant else self.plateau.cases[(x2, y2)].z0 for coord in self.plateau.geo.ligne((x1, y1, z1), (x2, y2, z2)): if coord != (x1, y1, z1): self._casesCibles.append(coord) if not self.estValide(): self._casesCibles = [] def estValide(self): """la ligne est valide si toutes les cases cibles ne sont pas occupees par autre chose que des combattants""" for x, y, zA in self._casesCibles: occupant = self.plateau.cases[(x, y)].occupant(zA) if occupant: if occupant <= 0: return False return True class Disque(Zone): """attaque de zone de forme circulaire""" def __init__(self): super(Disque, self).__init__() self._nom = "Attaque de zone (disque)" #decorateur def autorise(f): def _autorise(self, *args): def fVide(*args): pass retour = fVide if not self._desactivationDemandee: retour = f retour(self, *args) return _autorise def typeAttZone(self): return "disque" def activer(self, plateau, numPion): super(Disque, self).activer(plateau, numPion) @autorise def majCoordCible(self, coord): if self._coordCible in self.plateau.cases: self.plateau.cases[self._coordCible].majEstCibleCurseur(False) super(Disque, self).majCoordCible(coord) @autorise def majCibles(self): occupant = self.plateau.cases[self._coordCible].occupant() x, y = self._coordCible z = self.plateau.pions[occupant].zA() if occupant else self.plateau.cases[self._coordCible].z0 self._casesCibles = self.plateau.geo.zone3d( (x, y, z) , self._rayon) def afficherCibles(self, actif): if self.estValide(): super(Disque, self).afficherCibles(actif) else: super(Disque, self).afficherCibles(False) self.plateau.cases[self._coordCible].majEstCibleCurseur(actif, False) @autorise def majRayon(self, val): self._rayon = val if self.plateau: self.maj() @autorise def majItemsGraphiques(self): self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \ self.plateau.cases[self._coordCible].centreGraphique)) if self.estValide(): rect = self.rectEllipseCirculaire(self.plateau.cases[self._coordCible].centreGraphique, self._rayon) if rect != None: self._itemCible.setRect(rect) self._itemCible.setVisible(self.estValide() and rect != None) def estValide(self): return self.ldmValide() @autorise def rectEllipseCirculaire(self, centre, rayon): """renvoie le QRectF definissant une ellipse ayant le QPointF pour centre et le rayon en cases entres en param attention: l'ellipse n'est pas tout a fait circulaire, elle couvre horizontalement et verticalement le nombre de cases demandees""" rect = None if rayon > 0: p1 = QPointF((centre.x() - (rayon * self.plateau.hCase)), (centre.y() - (rayon * self.plateau.hCase))) p2 = QPointF((centre.x() + (rayon * self.plateau.hCase)), (centre.y() + (rayon * self.plateau.hCase))) if p1 != p2: rect = QRectF() rect.setTopLeft(p1) rect.setBottomRight(p2) return rect class Cone(Zone): """attaque de zone de forme conique""" def __init__(self): super(Cone, self).__init__() self._nom = "Attaque de zone: cône" def typeAttZone(self): return "cone" def description(self): return "Attaque à de zone (cone)" def majCibles(self): x0, y0 = self.acteur().position z0 = self.acteur().zA() + self.acteur().h x1, y1 = self._coordCible occupant = self.plateau.cases[self._coordCible].occupant() z1 = self.plateau.pions[occupant].zA() if occupant else self.plateau.cases[self._coordCible].z0 cone = self.plateau.geo.cone3d( (x0, y0, z0), (x1, y1, z1) ) if (x0, y0, z0) in cone: cone.remove((x0, y0, z0)) self._casesCibles = cone def creerItemsGraphiques(self): self._itemCible = QGraphicsPolygonItem() self._itemCible.setPen(QPen(QColor("black"))) self._itemCible.prepareGeometryChange() self.plateau.addItem(self._itemCible) def majItemsGraphiques(self): self._itemCible.setPolygon(self.polygoneCone(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \ self.plateau.cases[self._coordCible].centreGraphique)) def polygoneCone(self, point1, point2): """renvoie le polygone du cone defini par les deux points (origine, distance)""" ligne1 = QLineF(point1, point2) longueur = ligne1.length() ligne1.setAngle(ligne1.angle() + 22.5) ligne1.setLength(1.1547*longueur) ligne2 = QLineF(point1, point2) ligne2.setAngle(ligne2.angle() - 22.5) ligne2.setLength(1.1547*longueur) polygone = QPolygonF() polygone.append(point1) polygone.append(ligne1.p2()) polygone.append(ligne2.p2()) return polygone def itemLdm(): """retourne l'item graphique utilise pour les lignes de mire""" item = QGraphicsLineItem() item.setZValue(100) pinceau = QPen() pinceau.setColor(QColor(249, 249, 249)) pinceau.setStyle(Qt.DashDotLine) pinceau.setWidth(6) item.setPen(pinceau) return item def choisirAttaqueZone(): """affiche la boite de dialogue permettant un choix de la forme de l'attaque de zone""" fen = EcranAttaqueZone() fen.show() r = fen.exec_() return fen.resultat() if r == 1 else None class EcranAttaqueZone(QDialog): """boite de dialogue de parametrage de l'attaque de zone""" def __init__(self, parent=None): super (EcranAttaqueZone, self).__init__() self.createWidgets() def createWidgets(self): """construction de l'interface""" self.ui = Ui_zone_fenetre() self.ui.setupUi(self) self.connect(self.ui.zone_ligne, SIGNAL("clicked()"), self.ligne, Qt.UniqueConnection) self.connect(self.ui.zone_disque, SIGNAL("clicked()"), self.disque, Qt.UniqueConnection) self.connect(self.ui.zone_cone, SIGNAL("clicked()"), self.cone, Qt.UniqueConnection) self.setStyleSheet("DmLabel:hover {background: rgb(255, 255, 255)};") def resultat(self): return self._resultat def ligne(self): self._resultat = Ligne self.done(1) def disque(self): self._resultat = Disque self.done(1) def cone(self): self._resultat = Cone self.done(1) if __name__ == "__main__": app = QApplication(sys.argv) res = choisirAttaqueZone() print res exit()