#from __future__ import unicode_literals # -*- coding: utf-8 -*- from __future__ import division from PyQt4.QtCore import * from PyQt4.QtGui import * ##from ui.ecran_editionAttaques import Ui_editionAttaques import Modes import Actions from Case import Case from Combattant import Combattant from Decor import Decor from Pinceau import Pinceau from ProjectionDep import ProjectionDep from Cache import Cache from Terrain import Terrain from EcranCreerPlateau import EcranCreerPlateau from EcranEditionCombattant import EcranEditionCombattant from EcranEditionDecor import EcranEditionDecor from EcranEditionTerrain import EcranEditionTerrain from EcranAffichageTexte import EcranAffichageTexte from EcranGestionCombat import EcranGestionCombat from EcranSelectionPj import EcranSelectionPj from EcranVol import EcranVol import regles as regles from outilsSvg import enregistrer, charger, chargerUnique from time import time import br from math import sqrt m_couleursRapides = [(255,255,255), (200,200,200), (130,130,130), (90,90,90), (15,15,15), \ (0,85,0), (170,255,0), (170,255,127), (85,85,0), (85,0,0), (170,85,0), (100,50,0), \ (255,255,127), (240,80,0), (85,170,255), (85,85,255), (85,0,255), (0,255,255)] class Plateau(QGraphicsScene): """plateau de jeu contenant les cases, decors et pions""" def __init__(self, fenetre, parent=None): super(Plateau, self).__init__() #parametres et variables self.fenetre = fenetre self.id = "" ##partie et infos plateau self.nom = "" self.chapitre = 0 self.tour = 0 self.enCours = False self.public = False self.dateCreation = "" self.dateSvg = "" self.notes = "" self.presentation = "" self.description = "" #carac self.nbCasesX = 0 self.nbCasesY = 0 self.hCase = 0 self.couleurInit = QColor(0, 255, 0, 80) #objets self.pinceau = Pinceau(self) self.cases = {} #dict des cases du plateau (coordonnées: case) self.combattants = {} #liste de combattants positionnes sur le plateau self.decors = {} #liste des decors places sur le plateau self.caches = {} self.listeCasesZonePlacement = [] self.polygoneZonePlacement = None self.entreesSorties = [] #infos combat self.numCombattantEnCours = 0 self.ordreJeu = {} #numero du pion: ordre de jeu #note: la hauteur Z (qui gere l'empilement des objets graphiques est distribuee de cette maniere: #cases : 0 a 9 #pions : 10 et + def __getstate__(self): self.dateSvg = time() state = {key:value for key, value in self.__dict__.items() if not key in ["fenetre", "modeActif", \ "editionTerrain", "editionCreature", "editionDecor", \ "polygoneZonePlacement", "gestionCombat", \ "editionAttaques", "pinceau"]} return (state) def __setstate__(self, state): self.__dict__ = state def vue(self): return self.fenetre.ui.cbt_vue # def creer(self, idPlateau, nom, chapitre, formeCases, nbCasesX, nbCasesY, \ # couleur = QColor(0, 255, 0, 80), description = "", presentation = ""): def creer(self): """cree le plateau""" self.dateCreation = time() self.hCase = 120 #hauteur d'une case self.modeActif = Modes.ModeBase(self) self.gestionCombat = None self.initialisationGraphique() self.connexions() #cree les cases hexagonales for x in range(self.nbCasesX): for y in range(self.nbCasesY): c = Case(self) c.creer(x, y, self.couleurInit) self.cases[(x,y)] = c self.plateauModeCreation() def recreer(self, fenetre): self.fenetre = fenetre self.gestionCombat = None super(Plateau, self).__init__() self.modeActif = Modes.ModeBase(self) self.connexions() self.initialisationGraphique() #recreation des cases for coord in self.cases: self.cases[coord].recreer(self) #recreation des caches self.initListeCaches() #recreation des pions for numCombattant in self.combattants: self.combattants[numCombattant].ajouterAuPlateau(self) self.majOrdreJeu() #recreation des decors for num in self.decors: self.decors[num].ajouterAuPlateau(self) #recreation des marqueurs entree/sortie for entreeSortie in self.entreesSorties: entreeSortie.recreer(self) #recreation de la zone de placement: if len(self.listeCasesZonePlacement) > 0: self.polygoneZonePlacement = None self.majZonePlacement(self.listeCasesZonePlacement) self.plateauModeCombat() def fermer(self): """ferme le plateau 'proprement'""" self.miniature() self.pinceau = None for item in self.items(): item.prepareGeometryChange() self.removeItem(item) if self.gestionCombat != None: del self.gestionCombat self.fenetre.reinitialiserPanneauxPlateau() def miniature(self): """renvoie une miniature du plateau (QPixMap compresse) qui sera enregistree avec les infos de la sauvegarde""" ## img = QImage(128, 128, QImage.Format_ARGB32_Premultiplied) img = QPixmap(1024, 768) img.fill(QColor("white")) peintre = QPainter(img) self.render(peintre) peintre.end() img.scaledToHeight(128, Qt.FastTransformation) def connexions(self): """connecte le plateau aux differents widgets de la fenetre principale""" #modes d'interaction self.fenetre.connect(self.fenetre.ui.cbt_modeCreation, SIGNAL("clicked()"), self.plateauModeCreation, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cbt_modeCombat, SIGNAL("clicked()"), self.plateauModeCombat, Qt.UniqueConnection) ## self.fenetre.connect(self.fenetre.ui.modeAffichagePlateau, SIGNAL("currentIndexChanged(int)"), self.majModeAffichage, Qt.UniqueConnection) #etapes du combat self.fenetre.connect(self.fenetre.ui.cbt_etapeSuivante, SIGNAL("clicked()"), self.etapeSuivante, Qt.UniqueConnection) #affichage de fenetres self.fenetre.connect(self.fenetre.ui.cbt_nom, SIGNAL("clicked()"), self.afficherEcranPlateau, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cbt_afficherGestion, SIGNAL("clicked()"), self.afficheEcranGestionCombat, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_listeAttributs, SIGNAL("cellChanged(int,int)"), self.listeAttributCelluleModifiee, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_dialogueCouleurs, SIGNAL("clicked()"), self.modePeintureCase, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_pipetteCouleur, SIGNAL("clicked()"), self.modeCopieTerrain, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_afficherNotes, SIGNAL("clicked()"), self.agrandirNotesMjPlateau, Qt.UniqueConnection) #listes self.fenetre.connect(self.fenetre.ui.inf_listeOrdreJeu, SIGNAL("cellClicked(int,int)"), self.clicListOrdreJeu, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_listeTerrains, SIGNAL("cellClicked(int,int)"), self.modeMajTerrainCase, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_listeCreatures, SIGNAL("cellClicked(int,int)"), self.modeCreationCombattant, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_listeDecors, SIGNAL("cellClicked(int,int)"), self.modeCreationDecor, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_editerTerrain, SIGNAL("clicked()"), self.terrainEdit, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_nouveauTerrain, SIGNAL("clicked()"), self.terrainNouveau, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_editerCombattant, SIGNAL("clicked()"), self.creatureEdit, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_nouveauCombattant, SIGNAL("clicked()"), self.creatureNouveau, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_gommeCombattant, SIGNAL("clicked()"), self.majModeSupprimerCombattant, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_editerDecor, SIGNAL("clicked()"), self.decorEdit, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_nouveauDecor, SIGNAL("clicked()"), self.decorNouveau, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_gommeDecor, SIGNAL("clicked()"), self.majModeSupprimerDecor, Qt.UniqueConnection) #creation self.fenetre.connect(self.fenetre.ui.cp_epaisseurPinceau, SIGNAL("valueChanged(int)"), self.majEpaisseurPinceau, Qt.UniqueConnection) #autres: self.fenetre.connect(self.fenetre.ui.cp_placerEntree, SIGNAL("clicked()"), self.majModeDefinirEntree, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_placerSortie, SIGNAL("clicked()"), self.majModeDefinirSortie, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_defPlacement, SIGNAL("clicked()"), self.majModeZonePlacement, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_notes, SIGNAL("textChanged()"), self.majNotesCombattant, Qt.UniqueConnection) #formes (dessin) self.fenetre.connect(self.fenetre.ui.cp_formeSimple, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_formeLigne, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_formeLigneOrientee, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_formePot, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_formeRectVide, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.cp_formeRectPlein, SIGNAL("clicked()"), self.majModeForme, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_deplacement, SIGNAL("clicked()"), self.majModeCombatDeplacement, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_vol, SIGNAL("clicked()"), self.majModeCombatVol, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_attaqueCac, SIGNAL("clicked()"), self.majModeCombatAttaqueCaC, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_attaqueDist, SIGNAL("clicked()"), self.majModeCombatAttaqueDist, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_attaqueZone, SIGNAL("clicked()"), self.majModeCombatZone, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_formeAttaqueZone, SIGNAL("activated (int)"), self.majModeCombatZone, Qt.UniqueConnection) self.fenetre.connect(self.fenetre.ui.pi_rayonAttaqueZone, SIGNAL("valueChanged(int)"), self.majRayonZone, Qt.UniqueConnection) # self.fenetre.connect(self.fenetre.ui.pi_finTour, SIGNAL("clicked()"), self.pionSuivant, Qt.UniqueConnection) def initialisationGraphique(self): """cree la scene graphique et les parametres necessaires a son fonctionnement, et met a jour l'interface""" #on cree la scene graphique kx = 1 if self.formeCases == "H": kx = 0.866 marge = 240 self.setSceneRect(0 - marge, 0 - marge, (kx*self.hCase*(self.nbCasesX+2)) + marge, (self.hCase*(self.nbCasesY+2)) + marge) self.vue().setScene(self) self.vue().scale(0.25, 0.25) self.vue().centerOn(QPointF(0,0)) self.vue().setDragMode(1) self.setItemIndexMethod(QGraphicsScene.BspTreeIndex) self.polygoneZonePlacement = None #gestion du mode d'interaction avec le plateau self.pinceau = Pinceau(self) self.proj = ProjectionDep(self) #mise a jour de l'interface de creation self.fenetre.afficherPanneauxPlateau(True) self.majNomPlateau() self.majBoutonsCouleursPerso() self.fenetre.ui.cp_listeTerrains.defFichier("lib\\biblio\\terrain") self.fenetre.ui.cp_listeTerrains.initAffichage() self.fenetre.ui.cp_listeCreatures.defFichier("lib\\biblio\\combattant") self.fenetre.ui.cp_listeCreatures.initAffichage() self.fenetre.ui.cp_listeDecors.defFichier("lib\\biblio\\decor") self.fenetre.ui.cp_listeDecors.initAffichage() self.majListeTerrains() self.majListeCreatures() self.majListeDecors() self.initListeCaches() self.initListeOrdreJeu() self.initListeAttaques() self.majBoutonEtape() self.fenetre.ui.pi_deplacement.setCheckable(True) self.fenetre.ui.pi_attaqueCac.setCheckable(True) self.fenetre.ui.pi_attaqueDist.setCheckable(True) self.fenetre.ui.pi_attaqueZone.setCheckable(True) #mise a jour de l'interface d'informations self.majInfosCombattant(None) self.majInfosDecor(None) def estCree(self): """renvoie vrai si des cases ont ete creees""" return (len(self.cases) > 0) def afficherEcranPlateau(self): """affiche la fenetre de description du plateau""" ecran = EcranCreerPlateau() ecran.afficher(self) r = ecran.exec_() if r == 1: res = ecran.resultat() self.nom = res.nom self.chapitre = res.chapitre self.description = res.description self.presentation = res.presentation self.majNomPlateau() def majNomPlateau(self): """met a jour l'affichage du nom du plateau""" txt = self.nom if not self.public: txt += " [Non Publié]" self.fenetre.ui.cbt_nom.setText(QString().fromUtf8(" {} ".format(txt))) def majBoutonEtape(self): """met a jour le label et l'effet du bouton d'etape (en bas a droite)""" if not self.public: self.fenetre.ui.cbt_etapeSuivante.setText(QString().fromUtf8("Publier le plateau")) return if not self.tour > 0: self.fenetre.ui.cbt_etapeSuivante.setText(QString().fromUtf8("Démarrer le combat")) return self.fenetre.ui.cbt_etapeSuivante.setText(QString().fromUtf8("Tour {}\nFinir son tour".format(self.tour))) def etapeSuivante(self): """apres clic sur le bouton d'etape, on passe a l'etape suivante (publication, demarrage du combat...)""" if not self.public: self.publier() return if not self.tour > 0: self.demarrer() return self.pionSuivant() def publier(self): self.public = True self.majBoutonEtape() def demarrer(self): self.tour = 1 self.majBoutonEtape() def majBoutonsCouleursPerso(self): """met a jour l'affichage des couleurs customisees dans la boite de dialogue de selection de couleur""" for i in range(0,18): couleur = QColor() r, g, b = m_couleursRapides[i] couleur.setRgb(r, g, b) bouton = self.fenetre.ui.cp_boiteCouleurs.findChild(QToolButton, "cp_couleur{}".format(i+1)) if couleur.isValid(): bouton.setStyleSheet("QToolButton {backGround:%s}"%(couleur.name())) self.fenetre.connect(bouton, SIGNAL("clicked()"), self.modePeintureCase_perso) def chercherCouleur(self): """ouvre la boite de dialogue de selection de couleur""" couleur = QColorDialog(self.vue()).getColor(QColor("white"), self.vue()) return couleur def majListesPions(self, numCombattant = None): """met a jour les listes contenant des donnees liees aux pions""" ## self.majListeOrdreJeu() ## ## if numCombattant == None or numCombattant == self.pionSelectionne().numero: ## self.majListeAttributs() ## QApplication.processEvents() pass def ajouterPj(self): """affiche la fenetre de selection des pj, et recupere l'eventuel pj selectionne""" self.ecranSelPj = EcranSelectionPj(self.fenetre) self.ecranSelPj.setAttribute(Qt.WA_DeleteOnClose) listePj = chargerUnique("parties\\{}\\groupe".format(self.fenetre.partie)) self.ecranSelPj.charger(listePj) r = self.ecranSelPj.exec_() if r == 1: idPj = int(self.ecranSelPj.selection()) if idPj != None: pj = listePj[idPj] self.activerMode(Modes.CreationPion, pj) self.ecranSelPj = None def agrandirNotesMjPlateau(self): """affiche les notes du plateau dans une QDialog, puis recupere les donnees qui y sont saisies""" affichageTexte = EcranAffichageTexte(self.notes) affichageTexte.setAttribute(Qt.WA_DeleteOnClose) affichageTexte.exec_() self.notes = affichageTexte.recupererTexte() ##### affichage de la liste des terrains enregistres, et fonctions d'acces aux donnees"""" def afficheEcranEditionTerrains(self, terrain = None): """affiche l'ecran d'edition/creation de terrains""" self.editionTerrain = EcranEditionTerrain(terrain) self.editionTerrain.setAttribute(Qt.WA_DeleteOnClose) r = self.editionTerrain.exec_() if r == 1: self.majListeTerrains() self.activerMode(Modes.StandardCp) def majListeTerrains(self): """mise a jour de la liste des terrains depuis la sauvegarde""" self.fenetre.ui.cp_listeTerrains.maj() def terrainEdit(self): """ouvre la fenetre 'terrains' en mode edition""" index = self.fenetre.ui.cp_listeTerrains.item(self.fenetre.ui.cp_listeTerrains.currentRow(), 0) if index > 0: terrain = charger("lib\\biblio\\terrain", str(index.text().toUtf8())) self.afficheEcranEditionTerrains(terrain) self.activerMode(Modes.StandardCp) def terrainNouveau(self): """ouvre la fenetre 'terrains' en mode edition""" self.afficheEcranEditionTerrains() self.activerMode(Modes.StandardCp) ############### ##### affichage de la liste des creatures enregistrees, et fonctions d'acces aux donnees"""" def afficheEcranEditionCombattants(self, creature = None): """affiche l'ecran d'edition/creation de creatures""" self.editionCreature = EcranEditionCombattant(creature, 0, self.formeCases) self.editionCreature.setAttribute(Qt.WA_DeleteOnClose) r = self.editionCreature.exec_() if r == 1: combattant = self.editionCreature.combattant enregistrer(combattant.id, combattant, "lib\\biblio\\combattant") self.majListeCreatures() self.activerMode(Modes.StandardCp) self.editionCreature = None def majListeCreatures(self): """mise a jour de la liste des creatures depuis la sauvegarde""" self.fenetre.ui.cp_listeCreatures.maj() def creatureEdit(self): """ouvre la fenetre 'creatures' en mode edition""" index = self.fenetre.ui.cp_listeCreatures.item(self.fenetre.ui.cp_listeCreatures.currentRow(), 0) if index > 0: creature = charger("lib\\biblio\\combattant", str(index.text().toUtf8())) self.afficheEcranEditionCombattants(creature) self.activerMode(Modes.StandardCp) def creatureNouveau(self): """ouvre la fenetre 'creatures' en mode edition""" self.afficheEcranEditionCombattants() self.activerMode(Modes.StandardCp) ############### ##### affichage de la liste des decors enregistrees, et fonctions d'acces aux donnees"""" def afficheEcranEditionDecors(self, decor = None): """affiche l'ecran d'edition/creation de decors""" self.editionDecor = EcranEditionDecor(decor, 0, self.formeCases) self.editionDecor.setAttribute(Qt.WA_DeleteOnClose) r = self.editionDecor.exec_() if r == 1: decor = self.editionDecor.decor enregistrer(decor.id, decor, "lib\\biblio\\decor") self.majListeDecors() self.activerMode(Modes.StandardCp) def majListeDecors(self): """mise a jour de la liste des decors depuis la sauvegarde""" self.fenetre.ui.cp_listeDecors.maj() def decorEdit(self): """ouvre la fenetre 'decors' en mode edition""" self.afficheEcranEditionDecors(self.fenetre.ui.cp_listeDecors.actuel()) self.activerMode(Modes.StandardCp) def decorNouveau(self): """ouvre la fenetre 'decors' en mode edition""" self.afficheEcranEditionDecors() self.activerMode(Modes.StandardCp) ############### ############### maj des infos du panneau Pi a la selection/deselection d'un pion #voir a balancer tout ca dans une classe a part def majPanneauPi(self): if self.pionSelectionne(): estCombattant = (self.pionSelectionne().numero < 10000) #affichage self.fenetre.ui.pi_actions.setEnabled(estCombattant) self.fenetre.ui.pi_finTour.setEnabled(estCombattant) self.fenetre.ui.pi_ongletsListes.setTabEnabled(0, estCombattant) self.fenetre.ui.pi_ongletsListes.setTabEnabled(1, estCombattant) ### maj la selection dans la liste d'ordre de jeu if estCombattant: for i in range(0, self.fenetre.ui.inf_listeOrdreJeu.rowCount()): if str(self.fenetre.ui.inf_listeOrdreJeu.item(i, 0).text().toUtf8()) == str(self.pionSelectionne().numero): self.fenetre.ui.inf_listeOrdreJeu.setCurrentCell(i,0) ### maj des infos dans le panneau pi self.fenetre.ui.pi_nom.majTexte(self.pionSelectionne().txtId()) self.fenetre.ui.pi_img.chargerImage(self.pionSelectionne().img.rimage) ### maj de la liste des attributs if estCombattant: self.fenetre.ui.pi_listeAttributs.setColumnWidth(0, 50) self.disconnect(self.fenetre.ui.pi_listeAttributs, SIGNAL("cellChanged(int,int)"), self.listeAttributCelluleModifiee) #on vide la liste while self.fenetre.ui.pi_listeAttributs.rowCount() > 0: self.fenetre.ui.pi_listeAttributs.removeRow(0) #creation des lignes de base lignesBase = ["Nom","Etat","Alt."] #attention: modifier aussi dans listeAttributCelluleModifiee for i in range(0, 10): #10 premieres colonnes reservees pour les infos de base self.fenetre.ui.pi_listeAttributs.insertRow(i) item = QTableWidgetItem() if i < len(lignesBase): item.setText(QString.fromUtf8(lignesBase[i])) item.setFlags(Qt.NoItemFlags) self.fenetre.ui.pi_listeAttributs.setItem(i, 0, item) self.fenetre.ui.pi_listeAttributs.setRowHidden(i, (i >= len(lignesBase))) #maj des donnees de base self.fenetre.ui.pi_listeAttributs.setItem(lignesBase.index("Nom"), 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().nom)))) self.fenetre.ui.pi_listeAttributs.setItem(lignesBase.index("Etat"), 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().etat)))) self.fenetre.ui.pi_listeAttributs.setItem(lignesBase.index("Alt."), 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().z)))) #attributs issus des regles utilisees ordre = regles.ordreAttributs() for elt in ordre: ligne = 10 + ordre.index(elt) self.fenetre.ui.pi_listeAttributs.insertRow(ligne) item = QTableWidgetItem(QString.fromUtf8(elt)) item.setFlags(Qt.NoItemFlags) self.fenetre.ui.pi_listeAttributs.setItem(ligne, 0, item) self.fenetre.ui.pi_listeAttributs.setItem(ligne, 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().listeAttributs[elt])))) self.connect(self.fenetre.ui.pi_listeAttributs, SIGNAL("cellChanged(int,int)"), self.listeAttributCelluleModifiee) ###affiche les notes du combattant self.fenetre.ui.pi_notes.majTexte(self.pionSelectionne().notes) ###maj la liste des attaques du pion if estCombattant: #on vide la liste while self.fenetre.ui.pi_listeAttaques.rowCount() > 0: self.fenetre.ui.pi_listeAttaques.removeRow(0) i = 0 for attaque in self.pionSelectionne().attaques: self.fenetre.ui.pi_listeAttaques.insertRow(i) self.fenetre.ui.pi_listeAttaques.setItem(i, 0, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().attaques.index(attaque))))) icone = None if attaque.typ == "cac": icone = QIcon(":/interface/16/ressource/epee_16.png") if attaque.typ == "dist": icone = QIcon(":/interface/16/ressource/arc_16.png") if attaque.typ == "zone": icone = QIcon(":/interface/16/ressource/baguette_16.png") if icone != None: self.fenetre.ui.pi_listeAttaques.setItem(i, 1, QTableWidgetItem(icone, QString.fromUtf8(""))) self.fenetre.ui.pi_listeAttaques.setItem(i, 2, QTableWidgetItem(QString.fromUtf8(attaque.nom))) else: #maj des infos dans le panneau pi self.fenetre.ui.pi_img.clear() self.fenetre.ui.pi_nom.majTexte("Pas de pion\nselectionné") #vide la liste des attributs self.disconnect(self.fenetre.ui.pi_listeAttributs, SIGNAL("cellChanged(int,int)"), self.listeAttributCelluleModifiee) while self.fenetre.ui.pi_listeAttributs.rowCount() > 0: self.fenetre.ui.pi_listeAttributs.removeRow(0) #vide la liste des attaques du pion while self.fenetre.ui.pi_listeAttaques.rowCount() > 0: self.fenetre.ui.pi_listeAttaques.removeRow(0) self.majInfosAttaqueEC() ##### activation des differents modes d'interaction avec le plateau et mises a jour des principaux parametres ####### def plateauModeCreation(self): self.activerMode(Modes.StandardCp) def plateauModeCombat(self): self.activerMode(Modes.StandardPi) def activerMode(self, mode, param = None): """desactive le mode actif et active le nouveau a la place""" self.modeActif.desactiver() self.modeActif = mode(self) self.modeActif.activer(param) ## def majModeAffichage(self, index): ## """met a jour le mode d'affichage""" ## nouveauMode = "" ## if index == 0: ## #passe a l'affichage standard ## pass ## elif index == 1: ## #passe en mode affichage de l'altitude ## nouveauMode = "altitude" ## elif index == 2: ## #affichage des terrains slt ## nouveauMode = "terrain" ## elif index == 3: ## #affichage tactique ## nouveauMode = "tactique" ## ## for coord in self.cases: ## self.cases[coord].majAffichageSpecial(nouveauMode) def modePeintureCase(self): """enclenche le mode peinture de case a partir de la couleur selectionnee""" couleur = self.chercherCouleur() if couleur.isValid(): terrain = Terrain() terrain.couleur = couleur self.activerMode(Modes.MajCases, terrain) else: self.activerMode(Modes.StandardCp) def modePeintureCase_perso(self): origine = self.sender().objectName() index = int(origine.replace("cp_couleur",""))-1 couleur = QColor() r, g, b = m_couleursRapides[index] couleur.setRgb(r, g, b) if couleur.isValid(): terrain = Terrain() terrain.couleur = couleur self.activerMode(Modes.MajCases, terrain) else: self.activerMode(Modes.StandardCp) def modeCopieTerrain(self): """enclenche le mode copie de case""" self.activerMode(Modes.Pipette) def modeCaseEffet(self, index): """enclenche le mode de mise a jour de l'effet actif des cases""" effets = ["brule", "eau", "glace", "poison", "aucun"] self.activerMode(Modes.MajCasesEffet, effets[index]) def modeCreationPion(self): """enclenche le mode de creation de pions simples""" self.majMode("pionCreation") def majModeSupprimerDecor(self): """enclenche le mode suppression de pions sur clic gauche (creatures ou decors)""" self.activerMode(Modes.SuppressionPion, Decor) def majModeSupprimerCombattant(self): """enclenche le mode suppression de pions sur clic gauche (creatures ou decors)""" self.activerMode(Modes.SuppressionPion, Combattant) def modeCreationDecor(self, ligne, col): """enclenche le mode de creation de decors depuis la liste des decors""" index = self.fenetre.ui.cp_listeDecors.item(ligne, 0) decor = charger("lib\\biblio\\decor", str(index.text().toUtf8())) self.activerMode(Modes.CreationPion, decor) def modeCreationCombattant(self, ligne, col): """enclenche le mode de creation de pions depuis la liste des creatures""" index = self.fenetre.ui.cp_listeCreatures.item(ligne, 0) creature = charger("lib\\biblio\\combattant", str(index.text().toUtf8())) self.activerMode(Modes.CreationPion, creature) def modeMajTerrainCase(self, ligne, col): """enclenche le mode permettant la mise a jour du terrain des cases""" index = self.fenetre.ui.cp_listeTerrains.item(ligne, 0) terrain = charger("lib\\biblio\\terrain", str(index.text().toUtf8())) if terrain: self.activerMode(Modes.MajCases, terrain) else: print "terrain invalide" def majEpaisseurPinceau(self, epaisseur): """met a jour l'epaisseur du pinceau (en cases)""" self.fenetre.ui.cp_valeurEpaisseurPinceau.majTexte(str(epaisseur)) self.pinceau.majEpaisseur(int(epaisseur)) def majModeForme(self): """met a jour la forme utilisee pour la peinture""" formes = {"cp_formeSimple": "simple", \ "cp_formeLigne": "ligne", \ "cp_formeLigneOrientee": "frontiere", \ "cp_formePot": "pot", \ "cp_formeEllipseVide": "ellipseV", \ "cp_formeEllipsePlein": "ellipseP", \ "cp_formeRectVide": "rectV", \ "cp_formeRectPlein": "rectP"} self.pinceau.majForme(formes[str(self.sender().objectName())]) def majModeCombatDeplacement(self): """active le mode de combat 'deplacement' (mode standard)""" self.modeActif.nouvelleAction(Actions.Deplacement) def majModeCombatVol(self): """active le mode de combat 'vol'""" self.modeActif.nouvelleAction(Actions.Vol) def majModeCombatAttaqueCaC(self): """active le mode de combat 'corps-a-corps'""" self.modeActif.nouvelleAction(Actions.Cac) def majModeCombatAttaqueDist(self): """active le mode de combat 'attaque a distance'""" self.modeActif.nouvelleAction(Actions.Distance) def majModeCombatZone(self): if self.fenetre.ui.pi_formeAttaqueZone.currentIndex() == 0: action = Actions.Ligne elif self.fenetre.ui.pi_formeAttaqueZone.currentIndex() == 1: action = Actions.Disque elif self.fenetre.ui.pi_formeAttaqueZone.currentIndex() == 2: action = Actions.Cone if action: self.modeActif.nouvelleAction(action) def majRayonZone(self, val): action = self.modeActif.action() if action: action.majRayon(val) def majModeDefinirEntree(self): self.activerMode(Modes.CreationEntreeSortie, "E") def majModeDefinirSortie(self): self.activerMode(Modes.CreationEntreeSortie, "S") def majModeZonePlacement(self): self.activerMode(Modes.ZonePlacement) ############### ########## Gestion du combat ############## def majAffichageTour(self): """met a jour l'affichage du tour en cours""" self.fenetre.ui.cbt_tour.majTexte("Tour: {}".format(self.tour)) def initListeOrdreJeu(self): """cree les colonnes et met en forme la table ordre jeu""" self.fenetre.ui.inf_listeOrdreJeu.setColumnWidth(2, 30) self.fenetre.ui.inf_listeOrdreJeu.hideColumn(0) self.fenetre.ui.inf_listeOrdreJeu.hideColumn(2) self.fenetre.ui.inf_listeOrdreJeu.setIconSize(QSize(30,20)) def majListeOrdreJeu(self): """met a jour la liste des pions infoOrdreJeu""" while self.fenetre.ui.inf_listeOrdreJeu.rowCount() > 0: self.fenetre.ui.inf_listeOrdreJeu.removeRow(0) index = 0 for num in self.ordreJeu: self.fenetre.ui.inf_listeOrdreJeu.insertRow(int(index)) self.fenetre.ui.inf_listeOrdreJeu.setItem(int(index), 0, QTableWidgetItem(QString.fromUtf8(str(num)))) icon = QIcon(self.combattants[num].logo.chemin()) item = QTableWidgetItem(icon, QString.fromUtf8(self.combattants[num].txtId())) self.fenetre.ui.inf_listeOrdreJeu.setItem(int(index), 1, item) self.fenetre.ui.inf_listeOrdreJeu.setItem(int(index), 2, QTableWidgetItem(QString.fromUtf8(str(self.ordreJeu[num])))) index += 1 self.fenetre.ui.inf_listeOrdreJeu.sizeHintForColumn(1) ## trierTable(self.fenetre.ui.infoOrdreJeu, 2, 0) def clicListOrdreJeu(self, ligne, col): """on a clique dans la liste d'ordre de jeu, le pion correspondant est selectionne et centre sur la carte""" numCombattant = int(self.fenetre.ui.inf_listeOrdreJeu.item(ligne, 0).text().toUtf8()) self.vue().centerOn(self.cases[self.combattants[numCombattant].position].centreGraphique) self.modeActif.clic_combattant(numCombattant) def pionSuivant(self): """selection du pion suivant dans la liste d'ordre de jeu""" if not len(self.combattants) > 0: return if self.numPionEnCours in self.combattants: suivant = self.ordreJeu[self.numPionEnCours] + 1 else: suivant = 1 if suivant > len(self.ordreJeu): self.tour += 1 self.majAffichageTour() suivant = 1 for num in self.ordreJeu: if self.ordreJeu[num] == suivant: numCombattant = num break for ligne in range(0, self.fenetre.ui.inf_listeOrdreJeu.rowCount()): item = self.fenetre.ui.inf_listeOrdreJeu.item(ligne, 0) item.setSelected(int(self.fenetre.ui.inf_listeOrdreJeu.item(ligne, 0).text().toUtf8()) == numCombattant) if int(item.text().toUtf8()) == numCombattant: self.fenetre.ui.inf_listeOrdreJeu.scrollToItem(item) self.vue().centerOn(self.cases[self.combattants[numCombattant].position].centreGraphique) self.pionSaisir(numCombattant) def afficheEcranGestionCombat(self): """affiche l'ecran de gestion du combat""" self.gestionCombat = EcranGestionCombat(self) self.gestionCombat.show() self.connect(self.fenetre, SIGNAL("majListesPions"), self.majListesPions) self.connect(self.gestionCombat, SIGNAL("majListesPions"), self.majListesPions) QApplication.processEvents() ## r = self.gestionCombat.exec_() def majOrdreJeu(self): """met a jour l'ordre de jeu des pions en fonction de l'attribut prevu par les regles s'il existe, ou en fonction de l'ordre de jeu parametre sinon""" if len(self.ordreJeu) > 0: attribut = regles.attributOrdreJeu() if attribut != None: dico = {} for numCombattant in self.combattants: dico[numCombattant] = int(self.combattants[numCombattant].listeAttributs[attribut]) ordre = sorted(dico, key = dico.get, reverse=(regles.sensTriOrdreJeu() == 1)) self.ordreJeu = {} for numCombattant in self.combattants: self.ordreJeu[numCombattant] = ordre.index(numCombattant) + 1 self.majListeOrdreJeu() def pionDeplacerDansOrdreJeu(self, numCombattant, nouvellePosition): """deplace un pion dans le dictionnaire gerant l'ordre de jeu de maniere a assurer sa coherence nouvellePosition = 0 supprime le pion de la liste""" if numCombattant in self.ordreJeu: if nouvellePosition == 0: del self.ordreJeu[numCombattant] if len(self.ordreJeu) > 0: i = 0 tmp = sorted(self.ordreJeu, key=self.ordreJeu.get) if numCombattant in tmp: tmp.remove(numCombattant) for num in tmp: i += 1 if i == nouvellePosition: self.ordreJeu[numCombattant] = i i += 1 self.ordreJeu[num] = i if i < nouvellePosition: self.ordreJeu[numCombattant] = i + 1 elif nouvellePosition > 0: self.ordreJeu[numCombattant] = 1 self.majOrdreJeu() def listeAttributCelluleModifiee(self, ligne, colonne): """une cellule de la liste des attributs a ete modifiee""" if colonne != 1: print("valeur non modifiable") else: #on verifie la validite de la donnee entree lignesBase = ["Nom","Etat","Alt."] #attention: modifier aussi dans majListeAttribut if ligne < len(lignesBase): pass elif ligne >= 10: attribut = regles.ordreAttributs()[(ligne - 10)] nouvelleVal = str(self.fenetre.ui.pi_listeAttributs.item(ligne, 1).text().toUtf8()) valVerifiee = regles.listeControle()[attribut].controler(nouvelleVal) if valVerifiee != None: self.pionSelectionne().listeAttributs[attribut] = valVerifiee if attribut == regles.attributOrdreJeu(): print("maj ordre (a implementer)") else: self.fenetre.ui.pi_listeAttributs.setItem(ligne, 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().listeAttributs[attribut])))) def initListeAttaques(self): """met en forme et connecte la liste des attaques du pion""" self.fenetre.ui.pi_listeAttaques.setColumnWidth(0, 0) self.fenetre.ui.pi_listeAttaques.setColumnWidth(1, (0.3*self.fenetre.ui.pi_listeAttaques.width())) self.fenetre.ui.pi_listeAttaques.setColumnWidth(2, (0.7*self.fenetre.ui.pi_listeAttaques.width())) self.connect(self.fenetre.ui.pi_listeAttaques, SIGNAL("itemSelectionChanged()"), self.majInfosAttaqueEC) self.connect(self.fenetre.ui.pi_listeAttaques, SIGNAL("cellClicked(int, int)"), self.listeAttaquesCelluleCliquee) self.connect(self.fenetre.ui.pi_notesAttaqueEC, SIGNAL("textChanged()"), self.majNotesAttaqueEC) self.fenetre.ui.pi_listeAttributsAttaqueEC.setColumnWidth(0, (0.49*self.fenetre.ui.pi_listeAttributsAttaqueEC.width())) self.fenetre.ui.pi_listeAttributsAttaqueEC.setColumnWidth(1, (0.5*self.fenetre.ui.pi_listeAttributsAttaqueEC.width())) def majListeAttaques(self): """met a jour la liste des attaques du pion dans le panneau de combat""" #on vide la liste while self.fenetre.ui.pi_listeAttaques.rowCount() > 0: self.fenetre.ui.pi_listeAttaques.removeRow(0) self.fenetre.ui.pi_listeAttaques.setVisible((self.pionSelectionne() != None)) ## self.fenetre.ui.editerAttaques.setVisible((self.pionSelectionne() != None)) self.fenetre.ui.pi_panneauAttaqueEC.setVisible((self.pionSelectionne() != None)) i = 0 if self.pionSelectionne() != None: for attaque in self.pionSelectionne().attaques: self.fenetre.ui.pi_listeAttaques.insertRow(i) self.fenetre.ui.pi_listeAttaques.setItem(i, 0, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().attaques.index(attaque))))) icone = None if attaque.typ == "cac": icone = QIcon(":/interface/16/ressource/epee_16.png") if attaque.typ == "dist": icone = QIcon(":/interface/16/ressource/arc_16.png") if attaque.typ == "zone": icone = QIcon(":/interface/16/ressource/baguette_16.png") if icone != None: self.fenetre.ui.pi_listeAttaques.setItem(i, 1, QTableWidgetItem(icone, QString.fromUtf8(""))) self.fenetre.ui.pi_listeAttaques.setItem(i, 2, QTableWidgetItem(QString.fromUtf8(attaque.nom))) self.majInfosAttaqueEC() def listeAttaquesCelluleCliquee(self, ligne, colonne): """on a clique sur une cellule de la liste des attaques""" numAttaque = int(str(self.fenetre.ui.pi_listeAttaques.item(ligne, 0).text().toUtf8())) if numAttaque >= 0: self.utiliserAttaque(numAttaque) def utiliserAttaque(self, numAttaque): """le pion selectionne utilise son attaque n""" if self.pionSelectionne() != None: if numAttaque < len(self.pionSelectionne().attaques): attaque = self.pionSelectionne().attaques[numAttaque] if attaque.typ == "cac": self.majModeCombat("combatAttaqueCaC") if attaque.typ == "dist": self.majModeCombat("combatAttaqueDist") if attaque.typ == "zone": self.modeParam["typeAttaqueZone"] = attaque.formeZone self.fenetre.ui.pi_rayonAttaqueZone.setValue(attaque.rayon) self.majModeCombat("combatAttaqueZone") def majInfosAttaqueEC(self): """met a jour les infos de l'attaque en cours (selectionnee)""" selection = self.fenetre.ui.pi_listeAttaques.selectedItems() self.fenetre.ui.pi_panneauAttaqueEC.setVisible(self.pionSelectionne() != None and len(selection) > 0) if self.pionSelectionne() != None and len(selection) > 0: ligne = selection[0].row() numAttaque = int(str(self.fenetre.ui.pi_listeAttaques.item(ligne, 0).text().toUtf8())) self.disconnect(self.fenetre.ui.pi_panneauAttaqueEC, SIGNAL("cellChanged(int,int)"), self.attaqueECCelluleModifiee) #on vide la liste while self.fenetre.ui.pi_listeAttributsAttaqueEC.rowCount() > 0: self.fenetre.ui.pi_listeAttributsAttaqueEC.removeRow(0) self.fenetre.ui.pi_listeAttributsAttaqueEC.insertRow(0) self.fenetre.ui.pi_listeAttributsAttaqueEC.setItem(0, 0, QTableWidgetItem(QString.fromUtf8("numAtt"))) self.fenetre.ui.pi_listeAttributsAttaqueEC.setItem(0, 1, QTableWidgetItem(QString.fromUtf8(str(numAttaque)))) self.fenetre.ui.pi_listeAttributsAttaqueEC.setRowHidden(0, True) #attributs issus des regles utilisees ordre = regles.ordreAttributsAttaques() for elt in ordre: ligne = 1 + ordre.index(elt) self.fenetre.ui.pi_listeAttributsAttaqueEC.insertRow(ligne) item = QTableWidgetItem(QString.fromUtf8(elt)) item.setFlags(Qt.NoItemFlags) self.fenetre.ui.pi_listeAttributsAttaqueEC.setItem(ligne, 0, item) self.fenetre.ui.pi_listeAttributsAttaqueEC.setItem(ligne, 1, QTableWidgetItem(QString.fromUtf8(str(self.pionSelectionne().attaques[numAttaque].attributs[elt])))) self.connect(self.fenetre.ui.pi_listeAttributsAttaqueEC, SIGNAL("cellChanged(int,int)"), self.attaqueECCelluleModifiee) #maj des notes self.disconnect(self.fenetre.ui.pi_notesAttaqueEC, SIGNAL("textChanged()"), self.majNotesAttaqueEC) self.fenetre.ui.pi_notesAttaqueEC.majTexte(self.pionSelectionne().attaques[numAttaque].notes) self.connect(self.fenetre.ui.pi_notesAttaqueEC, SIGNAL("textChanged()"), self.majNotesAttaqueEC) def attaqueECCelluleModifiee(self, ligne, colonne): """une cellule de la liste d'attributs de l'attaque en cours a ete modifiee""" pass def majNotesAttaqueEC(self): """met a jour les notes de l'attaque en cours (selectionnee)""" selection = self.fenetre.ui.pi_listeAttaques.selectedItems() self.fenetre.ui.pi_panneauAttaqueEC.setVisible(self.pionSelectionne() != None and len(selection) > 0) if self.pionSelectionne() != None and len(selection) > 0: ligne = selection[0].row() numAttaque = int(str(self.fenetre.ui.pi_listeAttaques.item(ligne, 0).text().toUtf8())) self.pionSelectionne().attaques[numAttaque].notes = str(self.fenetre.ui.pi_notesAttaqueEC.toPlainText().toUtf8()) def majNotesCombattant(self): """les notes du pion ont ete mises a jour""" if self.pionSelectionne() != None: self.pionSelectionne().notes = str(self.fenetre.ui.pi_notes.toPlainText().toUtf8()) else: pass ############### ### panneau d'info def majInfosCombattant(self, combattant=None): self.fenetre.ui.inf_boitePion.maj(combattant) def majInfosDecor(self, decor=None): self.fenetre.ui.inf_boiteDecor.maj(decor) def majInfosCase(self, case=None): self.fenetre.ui.inf_boiteCase.maj(case) ############### fonctions de calcul ################ def coordonneesValides(self, coord): """les coordonnees entrees en parametre sont elles celles d'une case du plateau""" return (coord[0] >= 0 and coord[1] >= 0 and coord[0] < self.nbCasesX and coord[1] < self.nbCasesY) def lstCoordAdjacentes(self, x, y): """renvoie la liste des coordonnees adjacentes, !!!! sans condition d'existence sur le plateau !!!! attention: l'ordre est important""" if self.formeCases == "H": if 1 == (x % 2): voisins = [(x, y-1), (x+1, y), (x+1, y+1), (x, y+1), (x-1, y+1), (x-1, y)] else: voisins = [(x, y-1), (x+1, y-1), (x+1, y), (x, y+1), (x-1, y), (x-1, y-1)] else: voisins = [(x, y-1), (x+1, y), (x, y+1), (x-1, y)] return voisins def zone(self, origine, rayon, z=0, conditionFranchissable = False, conditionVisible = False): """renvoie un dictionnaire representant la liste des coordonnees des cases comprises dans la zone la zone en question est la liste des cases situees a une distance d des coordonnees d'origine z = 0 -> hauteur z de l'origine par rapport a l'altitude de la case conditionFranchissable = Vrai -> les cases infranchissables ne sont pas prises en compte conditionVisible = Vrai -> les cases bloquant la visibilite ne sont pas prises en compte""" aVerifier = [] aVerifier2 = [] resultat = {} k = 0 #on part de la premiere case, puis on itere a partir de chaque nouveau depart sur les voisins if origine in self.cases: aVerifier.append(origine) while k <= rayon: for depart in aVerifier: for coord in self.cases[depart].voisins: if not coord in aVerifier and not coord in aVerifier2 and not coord in resultat: if conditionFranchissable and not conditionVisible: if self.cases[coord].estFranchissable(z): aVerifier2.append(coord) elif not conditionFranchissable and conditionVisible: if self.cases[coord].terrain.visibilite: aVerifier2.append(coord) elif conditionFranchissable and conditionVisible: if self.cases[coord].estFranchissable(z) and self.cases[coord].terrain.visibilite: aVerifier2.append(coord) else: aVerifier2.append(coord) for elt in aVerifier: resultat[elt] = k aVerifier = aVerifier2 aVerifier2 = [] k += 1 return resultat def zone3d(self, origine, rayon, zCible=0): """renvoie les cases de la zone au format (x, y, z)""" retour = [] zone = self.zone(origine, rayon) for coord in zone: x, y = coord dz = rayon - zone[coord] for z in range(-dz, dz + 1): retour.append( (x, y, (zCible+z) ) ) return retour def cone(self, coord1, coord2): """renvoie les coord des cases composant le cone (en 2d)""" retour = [] x1, y1 = coord1 x2, y2 = coord2 if x1 % 2 == 1: y1 += 0.5 if x2 % 2 == 1: y2 += 0.5 if abs(y2 - y1) - abs(x2 - x1) <= 0 : #secteur horizontal angleRef = (y2 - y1) / (x2 - x1) dist2Ref = ( (x2 - x1)**2 + (y2 - y1)**2 ) for coord in self.cases: x, y = coord if x % 2 == 1: y += 0.5 if x != x1: angle = (y - y1) / (x - x1) if abs( angleRef - angle ) <= 0.5 and ( (x - x1)**2 + (y - y1)**2 ) <= dist2Ref \ and ( (x - x1) * (x2 - x1) ) > 0: retour.append(coord) else: #secteur vertical angleRef = (x2 - x1) / (y2 - y1) dist2Ref = ( (x2 - x1)**2 + (y2 - y1)**2 ) for coord in self.cases: x, y = coord if x % 2 == 1: y += 0.5 if y != y1: angle = (x - x1) / (y - y1) if abs( angleRef - angle ) <= 0.5 and ( (x - x1)**2 + (y - y1)**2 ) <= dist2Ref \ and ( (y - y1) * (y2 - y1) ) > 0: retour.append(coord) return retour def cone3d(self, coord1, coord2, z1 = 0, z2 = 0): return self.cone(coord1, coord2) def polygoneAgglo(self, listeCases): """renvoie un polygone contruit par agglomeration des polygones des cases de la liste les cases doivent etre adjacentes (cases hexagonales ou carrees)""" pointsPolygone = [] segments = [] case = Case(self) #on verifie que toutes les cases sont adjacentes les unes aux autres valide = True if len(listeCases) > 1: for coord in listeCases: if not len(set(listeCases).intersection(self.lstCoordAdjacentes(coord[0], coord[1]))) > 0: valide = False if not len(listeCases) > 0: valide = False if valide: #on parcourt les faces des polygones des cases, et on ne garde que ceux qui n'ont pas de case 'en face' for coord in listeCases: polygone = case.polygone(coord[0], coord[1]) voisins = self.lstCoordAdjacentes(coord[0], coord[1]) for i in range(0, len(voisins)): if not voisins[i] in listeCases: j = i+1 if j > len(voisins) - 1: j = 0 segments.append(QLineF(polygone[i], polygone[j])) #on 'accroche' les segments les uns aux autres, dans l'ordre segments2 = [segments[0]] for segment2 in segments2: for segment in segments: if not QLineF(segment.p1(), segment.p2()) in segments2 and not QLineF(segment.p2(), segment.p1()) in segments2: if sqrt((segment.p1().x()-segment2.p2().x())**2+(segment.p1().y()-segment2.p2().y())**2) < 1: segments2.append(QLineF(segment.p1(), segment.p2())) elif sqrt((segment.p2().x()-segment2.p2().x())**2+(segment.p2().y()-segment2.p2().y())**2) < 1: segments2.append(QLineF(segment.p2(), segment.p1())) pointsPolygone = [] premierPoint = segments2[0].p1() pointsPolygone.append(premierPoint) for segment in segments2: pointSuivant = segment.p2() if pointSuivant != premierPoint: pointsPolygone.append(pointSuivant) #creation du polygone polygone = QPolygonF() for point in pointsPolygone: polygone.append(point) else: polygone = None return polygone def coordCentreListeCases(self, listeCases): """renvoie les coordonnees centrales d'une liste de cases""" retour = None if len(listeCases) > 0: listeTriee = sorted(listeCases) posMilieu = int(len(listeCases)/2) retour = listeTriee[posMilieu] return retour def coordonneesAuPoint(self, point): """renvoie les coordonnees de la case situee au QPointF entre en parametre""" coord = None if point != None: lstObjets = self.vue().scene().items(point) for objet in lstObjets: if objet: if objet.__class__.__name__ == "Case": coord = (objet.x, objet.y) break return coord def casesSousForme(self, forme, plein = True, epaisseur = 0): """renvoie la liste des cases en collision avec un QGraphicsItem en parametre plein = False: pas le contenu de la forme epaisseur = renvoie aussi les cases voisines jusqu'a la distance demandee""" tmp = [] listeCases = [] point1 = None point2 = None #point 1 et 2 if forme.__class__.__name__ == "QGraphicsLineItem": point1 = forme.line().p1() point2 = forme.line().p2() elif forme.__class__.__name__ == "QGraphicsRectItem" or forme.__class__.__name__ == "QGraphicsEllipseItem": point1 = forme.rect().topLeft() point2 = forme.rect().bottomRight() else: point1 = forme.boundingRect().topLeft() point2 = forme.boundingRect().bottomRight() #preselection des cases (meilleures perf) if point1 != None and point2 != None and point1 != point2: preSelection = self.preSelectionCollision(point1, point2) else: preSelection = [] for coord in self.cases: preSelection.append(coord) #on liste les cases en collision avec la forme for coord in preSelection: if self.cases[coord].collidesWithItem(forme, Qt.IntersectsItemShape): if plein: tmp.append(coord) else: contenu = True for i in range(0,len(self.cases[coord].polygon())): if not forme.contains(self.cases[coord].polygon().at(i)): contenu = False break if contenu == False: tmp.append(coord) #on applique l'epaisseur du pinceau (lignes ou formes vides seulement) si necessaire if not plein and epaisseur > 0: for coord in tmp: zone = self.zone(coord, epaisseur) for coord2 in zone: if not coord2 in listeCases: listeCases.append(coord2) else: listeCases = tmp #si la liste est vide, on ajoute l'origine de la forme if len(listeCases) == 0: listeCases = [self.coordonneesAuPoint(point1)] return listeCases def preSelectionCollision(self, point1, point2): """renvoie une liste des cases qui peuvent etre concernees par une collision avec un graphicsItem (pour des raisons de performance)""" preSelection = [] coord1 = self.coordonneesAuPoint(point1) coord2 = self.coordonneesAuPoint(point2) if coord1 != None and coord2 != None: minX = min(coord1[0], coord2[0]) - 1 maxX = max(coord1[0], coord2[0]) + 1 minY = min(coord1[1], coord2[1]) - 1 maxY = max(coord1[1], coord2[1]) + 1 for coord in self.cases: if coord[0] >= minX and coord[0] <= maxX and coord[1] >= minY and coord[1] <= maxY : preSelection.append(coord) else: preSelection = self.cases return preSelection def listeZCible(self, coord): """retourne l'altitude absolue a prendre en compte en fonction de la case ciblee c'est l'altitude la case si aucun pion n'occupe la case c'est la liste des cases occupees en hauteur par le pion sinon 'coord' est de la forme (x, y)""" if self.cases[coord].estOccupee(): retour = range(self.cases[coord].occupant().zAbs(), \ self.cases[coord].occupant().zAbs() + self.cases[coord].occupant().hauteur) else: retour = [self.cases[coord].altitude] return retour def estCibleAttaqueDistValide(self, coordCible): """la case cible est elle valide pour une attaque a distance depuis la position et hauteur du pion selectionne les coord sont de la forme (x, y)""" valide = False x1, y1 = self.pionSelectionne().position coordOrigine = (x1, y1, (self.pionSelectionne().zAbs() + self.pionSelectionne().hauteur)) x2, y2 = coordCible cible = self.cases[coordCible].occupant() listeZ2 = self.listeZCible(coordCible) if coordOrigine[0] != coordCible[0] or coordOrigine[1] != coordCible[1]: for z2 in listeZ2: zValide = True casesLigneMire = br.ligne(coordOrigine, (x2, y2, z2), self.formeCases) casesLigneMire.remove(coordOrigine) casesLigneMire.remove((x2, y2, z2)) for coord in casesLigneMire: if zValide: x, y, z = coord if self.cases[(x,y)].estOccupee(z): if self.cases[(x,y)].occupant(z) not in [self.pionSelectionne(), cible]: zValide = False if zValide: valide = True #si au moins un des z cibles est valide, la ligne de mire est valide break return valide def coutDep(self, coord1, coord2, z2 = 0): """renvoie le cout de deplacement de la case 1 a la case 2 pour le pion actuellement selectionne / un cout egal a -1 implique un deplacement impossible""" retour = 0 dist = 1 if not self.cases[coord2].terrain.franchissable: return -1 if self.cases[coord2].occupant(z2, Combattant): return -1 if self.pionSelectionne().z == 0: dz = self.cases[coord2].zDep() - self.cases[coord1].zDep() if dz < (-1 * self.pionSelectionne().saut): return -1 if dz > self.pionSelectionne().hauteur: #si la diff de hauteur est superieure a la hauteur du combattant, il escalade if not self.pionSelectionne().depEscalade > 0: return -1 retour += self.pionSelectionne().coutDep("depEscalade", dz) elif 0 < dz <= self.pionSelectionne().hauteur: dist += dz if self.cases[coord2].terrain.nage: if not self.pionSelectionne().depNage > 0: return -1 retour += self.pionSelectionne().coutDep("depNage", dist) else: if not self.pionSelectionne().depMarche > 0: return -1 retour += self.pionSelectionne().coutDep("depMarche", dist) else: if not self.pionSelectionne().depVol > 0: return -1 retour += self.pionSelectionne().coutDep("depVol", dist) return retour def pionSurCase(self, coord): """renvoie le pion present sur la case, none sinon""" retour = None for num in self.combattants: if self.combattants[num].position == coord: retour = num return retour def pionsSurListeCase(self, listeCases): """renvoie la liste des num des pions presents sur la liste de cases""" retour = [] for coord in listeCases: pion = self.cases[coord].occupant() if pion != None and not pion.numero in retour: retour.append(pion.numero) return retour def majZonePlacement(self, listeCases): """met a jour la forme et l'affichage de la zone de placement initale des joueurs""" if len(listeCases) > 0: if self.polygoneZonePlacement == None: self.polygoneZonePlacement = QGraphicsPolygonItem(scene=self) self.polygoneZonePlacement.setZValue(0) qCouleurFond = QColor("white") qCouleurFond.setAlpha(50) self.polygoneZonePlacement.setBrush(qCouleurFond) pinceau = QPen(QColor("orange")) pinceau.setWidth(20) self.polygoneZonePlacement.setPen(pinceau) self.polygoneZonePlacement.setAcceptedMouseButtons(Qt.NoButton) self.polygoneZonePlacement.setAcceptHoverEvents(False) self.addItem(self.polygoneZonePlacement) listeCases2 = [] for coord in listeCases: if self.cases[coord].estFranchissable(): listeCases2.append(coord) self.polygoneZonePlacement.setPolygon(self.polygoneAgglo(listeCases2)) self.listeCasesZonePlacement = listeCases def materialiserPions(self,actif): """avtive/desactive la reception par les pions (autres que le pion selectionne) des hover events""" for numCombattant in self.combattants: if numCombattant != self.modeParam["numPionSelectionne"]: self.combattants[numCombattant].setAcceptsHoverEvents(actif) self.combattants[numCombattant].polygoneGraphique.setAcceptsHoverEvents(actif) for numCombattant in self.decors: self.decors[numCombattant].setAcceptsHoverEvents(actif) self.decors[numCombattant].polygoneGraphique.setAcceptsHoverEvents(actif) ####################### ######## interaction avec les cases, decors et pions ############# def pionSelectionne(self): """renvoie le pion actuellement selectionne""" retour = None if self.modeActif.__class__.__name__ == "PionSelectionne": retour = self.modeActif.pion() return retour def caseCliquee(self, x, y): """on a clique sur la case (clic gauche)""" coord = (x, y) accepte = self.modeActif.clic_case(coord) return accepte def caseSurvolClicEnfonce(self, coord): """une case est survolee par le curseur (le clic gauche est enfonce)""" accepte = self.modeActif.survolClic_case(coord) return accepte def caseSurvol(self, x, y): """une case est survole par le curseur, on affiche ses informations dans la zone prevue""" self.majInfosCase(self.cases[(x,y)]) accepte = self.modeActif.survol_case((x,y)) return accepte def afficherListeCases(self, listeCases, actif): """met ou non en evidence les cases selectionnees""" for coord in listeCases: self.cases[coord].majEstCibleCurseur(actif) def pionClique(self, num): """on a clique sur ce pion""" if num < 10000: accepte = self.modeActif.clic_combattant(num) else: accepte = self.modeActif.clic_decor(num) return accepte def combattantSurvol(self, num): """le pion est survole par le curseur, on affiche ses informations dans la zone prevue""" accepte = self.modeActif.survol_combattant(num) pion = self.combattants[num] self.majInfosCombattant(pion) if not accepte: accepte = self.caseSurvol(pion.position[0], pion.position[1]) return accepte def combattantFinSurvol(self, num): """le pion est survole par le curseur, on affiche ses informations dans la zone prevue""" accepte = self.modeActif.finSurvol_combattant(num) self.majInfosCombattant(None) return accepte def decorSurvol(self, num): """le pion est survole par le curseur, on affiche ses informations dans la zone prevue""" accepte = self.modeActif.survol_decor(num) pion = self.decors[num] self.majInfosDecor(pion) if not accepte: accepte = self.caseSurvol(pion.position[0], pion.position[1]) return accepte def decorFinSurvol(self, num): """le pion est survole par le curseur, on affiche ses informations dans la zone prevue""" accepte = self.modeActif.finSurvol_decor(num) self.majInfosDecor(None) return accepte def pionDoubleClic(self, numCombattant): """on a double-clique sur le pion""" accepte = self.modeActif.doubleClic_combattant(numCombattant) return accepte def creerPion(self, pionModele): """creer un pion (combattant ou decor) aux coordonnees indiquees""" cree = False if self.proj.projectionValide(): if pionModele.__class__.__name__ == "Combattant": pion = Combattant() elif pionModele.__class__.__name__ == "Decor": pion = Decor() for elt in pionModele.__dict__: pion.__dict__[elt] = pionModele.__dict__[elt] if pionModele.__class__.__name__ == "Combattant": numero = 1 if len(self.combattants) > 0: numero = max(self.combattants) + 1 pion.numero = numero pion.numComplementaire = self.numeroterNom(pion.nom) self.combattants[numero] = pion self.pionDeplacerDansOrdreJeu(numero, len(self.ordreJeu) + 2) elif pionModele.__class__.__name__ == "Decor": numero = 10001 if len(self.decors) > 0: numero = max(self.decors) + 10001 pion.numero = numero self.decors[numero] = pion pion.position = self.proj.coord() pion.nbRotations = self.proj.nbRotations() pion.ajouterAuPlateau(self) cree = True return cree def numeroterNom(self, nom): """renvoie le nom du pion avec un numero complementaire si necessaire """ i = 1 for numCombattant in self.combattants: if self.combattants[numCombattant].nom == nom: i += 1 if i == 1: retour = "" else: retour = str(i) return retour def pionDeposer(self, coordCase): """on depose le pion sur la case voulue""" if self.pionSelectionne() != None: pion = self.pionSelectionne() if pion != None: if self.proj.projectionValide(): pion.majPosition(self.proj.coord(), self.proj.nbRotations()) def majZPion(self, valeur): """met a jour l'altitude du pion selectionne""" if self.pionSelectionne() != None: self.pionSelectionne().majZ(valeur) def dialogueVol(self, actuelle): ecran = EcranVol(actuelle) ecran.exec_() nouvelle = ecran.resultat() del ecran return nouvelle def pionSupprimer(self, num): """supprime le pion entre en parametre""" #settrace(trace_calls) if num in self.combattants: self.pionDeplacerDansOrdreJeu(num, 0) pionSuppr = self.combattants.pop(num) elif num in self.decors: pionSuppr = self.decors.pop(num) pionSuppr.retirerDuPlateau() ############### ######### caches ############### def activerModeCreationCache(self): self.activerMode(Modes.EditerCaches) def activerModeEditionCache(self): idCache = int(self.fenetre.ui.cp_listeCaches.texte(self.fenetre.ui.cp_listeCaches.currentRow(), 0)) self.activerMode(Modes.EditerCaches, idCache) def initListeCaches(self): """charge la liste des caches avec les donnees en memoire""" for idCache in self.caches: self.ajouterCacheATable(self.caches[idCache]) def nouveauCache(self, listeCases): nouvelId = 0 if len(self.caches) > 0: nouvelId = max(self.caches) + 1 cache = Cache(nouvelId) cache.activer(True) cache.nom = "Cache {}".format(nouvelId + 1) self.caches[nouvelId] = cache self.ajouterCacheATable(cache) for coord in listeCases: self.cases[coord].ajouterCache(nouvelId) def editerFormeCache(self, idCache, listeCases): """edite le cache avec la liste des cases en param""" actif = self.caches[idCache].actif() for coord in self.cases: if coord in listeCases: self.cases[coord].ajouterCache(idCache, actif) else: self.cases[coord].supprimerCache(idCache) def majEtatCache(self, actif): emetteur = self.sender().objectName() idCache = int(emetteur.replace("afficherCache_", "")) self.caches[idCache].activer(actif) if actif: nomImg = "oeilBarre2_32.png" else: nomImg = "oeil_32.png" self.fenetre.ui.cp_listeCaches.item(idCache, 1).setIcon(QIcon(":/interface/32/ressource/{}".format(nomImg))) for coord in self.cases: self.cases[coord].activerCache(idCache, actif) def majNomCache(self, ligne, colonne): idCache = int(self.fenetre.ui.cp_listeCaches.texte(ligne, 0)) nouveauNom = self.fenetre.ui.cp_listeCaches.texte(ligne, 1) if nouveauNom != self.caches[idCache].nom: self.caches[idCache].nom = nouveauNom def supprimerCache(self): ligne = self.fenetre.ui.cp_listeCaches.currentRow() idCache = int(self.fenetre.ui.cp_listeCaches.texte(ligne, 0)) for coord in self.cases: self.cases[coord].supprimerCache(idCache) self.fenetre.ui.cp_listeCaches.removeRow(idCache) del self.caches[idCache] def mettreCacheEnEvidence(self, ligne, colonne): idCache = int(self.fenetre.ui.cp_listeCaches.texte(ligne, 0)) for coord in self.cases: if len(self.cases[coord].cachesActifs) > 0: self.cases[coord].polygoneCache.activerSurbrillance((idCache in self.cases[coord].cachesActifs)) def ajouterCacheATable(self, cache): """ajoute le cache a la table des caches""" self.fenetre.disconnect(self.fenetre.ui.cp_listeCaches, SIGNAL("cellChanged(int,int)"), self.majNomCache) ligne = self.fenetre.ui.cp_listeCaches.nouvelleLigneFin() if cache.actif(): nomImg = "oeilBarre2_32.png" else: nomImg = "oeil_32.png" self.fenetre.ui.cp_listeCaches.majTexte(ligne, 0, str(cache.numero)) item = QTableWidgetItem(QIcon(":/interface/32/ressource/{}".format(nomImg)), cache.nom) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable) self.fenetre.ui.cp_listeCaches.setItem(ligne, 1, item) widget = QCheckBox(QString("")) widget.setChecked(cache.actif()) widget.setObjectName(QString("afficherCache_{}".format(ligne))) self.connect(widget, SIGNAL("toggled(bool)"), self.majEtatCache) self.fenetre.ui.cp_listeCaches.setCellWidget(ligne, 2, widget) self.fenetre.connect(self.fenetre.ui.cp_listeCaches, SIGNAL("cellChanged(int,int)"), self.majNomCache, Qt.UniqueConnection) ###############" ######### gestion des evenements souris et clavier ############### def mouseMoveEvent(self, event): super(Plateau, self).mouseMoveEvent(event) if event.buttons() == Qt.LeftButton and self.vue().dragMode() != QGraphicsView.ScrollHandDrag: coord = self.coordonneesAuPoint(event.scenePos()) if coord != None: self.caseSurvolClicEnfonce(coord) else: self.modeActif.mouvementSouris(event) event.ignore() def mousePressEvent(self, event): super(Plateau, self).mousePressEvent(event) if event.button() == 1: self.modeActif.clicGauche(event) elif event.button() == 2: self.modeActif.clicDroit(event) event.accept() def mouseReleaseEvent(self, event): super(Plateau, self).mouseReleaseEvent(event) self.modeActif.finClicGauche(event) def keyPressEvent(self, event): """gestion des evenements clavier""" self.modeActif.toucheClavier(event) ################