#from __future__ import unicode_literals # -*- coding: utf-8 -*- from PyQt4.QtCore import * from PyQt4.QtGui import * import regles import AEtoile import br from Pion import Pion from Combattant import Combattant from Decor import Decor class Action(object): """action effectuee par un combattant sur le plateau de jeu""" def __init__(self): self._num = None #no du pion actif 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 typeAtt(self): return "" def activer(self, plateau, numPion): self.plateau = plateau self._num = numPion self.enfoncerBouton(True) self.activerCurseur() self.creerItemsGraphiques() self._enCours = True def majCoordCible(self, coord): """met a jour les coordonnees de la cible, cad la case actuellement survolee par la souris""" if self.plateau.coordonneesValides(coord): self._coordCible = coord self.maj() def desactiver(self): self._desactivationDemandee = True self.afficherCibles(False) self.detruireItemsGraphiques() self.desactiverCurseur() self.enfoncerBouton(False) self._enCours = False 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.combattants[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 enfoncerBouton(self, actif): for bouton in self.plateau.fenetre.ui.pi_actions.findChildren(QToolButton): if bouton.objectName() == self._nomBouton: bouton.setChecked(actif) #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._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 = "pi_deplacement" def typeAtt(self): return "dep" 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.projectionValide(): 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].occupeeParCombattant(): self._chercheurChemin = AEtoile.Chemin(self.plateau, self.coordActeur(), self._coordCible, self.acteur().z, self._zCible) self._chemin = self._chercheurChemin.liste() self.afficherCibles(True) def afficherCibles(self, actif): compte = 0 ; coutTotal = 0 ; valide = True z = self.plateau.cases[self.acteur().position].zDep() 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].zDep() - z self.plateau.cases[coord].majAffichageDeplacement(compte, valide, dz) else: self.plateau.cases[coord].majAffichageDeplacement(0) z = self.plateau.cases[coord].zDep() 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().z 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._zCible = 0 self._nomBouton = "pi_vol" def typeAtt(self): return "vol" def recupZCible(self): nouveauZ = self.plateau.dialogueVol(self.acteur().z) 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._attributs = regles.listeAttributsAttaques() self._notes = "" def typeAtt(self): return "att" 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 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) class Cac(Attaque): """attaque au corps a corps""" def __init__(self): super(Cac, self).__init__() self._nom = "Attaque au corps-à-corps" self._pionCible = None self._sourceCurseur = "" self._nomBouton = "pi_attaqueCac" self._icone = "img\\curseurEpee.png" def typeAtt(self): return "cac" def desactiver(self): self.afficherCibles(False) super(Cac, self).desactiver() def valider(self): if self.estValide() and self._pionCible: super(Cac, self).valider() def maj(self): self.afficherCibles(False) pionCible = 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.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._pionCible = None self._sourceCurseur = "" self._nomBouton = "pi_attaqueDist" self._icone = ":/interface/16/ressource/arc_16.png" def typeAtt(self): return "dist" 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) pionCible = self.plateau.cases[self._coordCible].occupant() 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.plateau.estCibleAttaqueDistValide(self._coordCible) def afficherCibles(self, actif): valide = True if actif: valide = self.estValide() 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 = QGraphicsLineItem() self._itemLigne.setZValue(100) pinceau = QPen() pinceau.setWidth(6) self._itemLigne.setPen(pinceau) 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 = "pi_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 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: self.plateau.cases[(coord[0], coord[1])].majEstCibleCurseur(actif) 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) pion.estCibleAttaque(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().zAbs() + self.acteur().hauteur x2, y2 = self._coordCible if self.plateau.cases[(x2, y2)].estOccupee(): z2 = self.plateau.cases[(x2, y2)].occupant().zAbs() else: z2 = self.plateau.cases[(x2, y2)].altitude for coord in br.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): retour = True for coord in self._casesCibles: x, y, z = coord if not self.plateau.cases[(x, y)].estFranchissable(z): if not isinstance(self.plateau.cases[(x, y)].occupant(z), Combattant): retour = False break return retour 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) self._rayon = self.plateau.fenetre.ui.pi_rayonAttaqueZone.value() @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): if self.plateau.cases[self._coordCible].estOccupee(): zCible = self.plateau.cases[self._coordCible].occupant().zAbs() else: zCible = self.plateau.cases[self._coordCible].altitude self._casesCibles = self.plateau.zone3d(self._coordCible, self._rayon, zCible) 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 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.plateau.estCibleAttaqueDistValide(self._coordCible) @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 majCibles(self): if self.plateau.cases[self._coordCible].estOccupee(): zCible = self.plateau.cases[self._coordCible].occupant().zAbs() else: zCible = self.plateau.cases[self._coordCible].altitude self._casesCibles = self.plateau.cone3d(self.acteur().position, self._coordCible, \ (self.acteur().z + self.acteur().hauteur), zCible) 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