Esempio n. 1
0
 def mouseReleaseEvent(self, e):
     if self.currentTerritory:
         if self.game.state == State.InitialPlacement:
             self.territoryClaimed.emit(self.currentTerritory.name)
             return
         m = e.modifiers()
         if m & Qt.ShiftModifier:
             n = 3
         elif m & Qt.AltModifier:
             n = 10
         else:
             n = 1
         if self.game.state in (State.InitialDraft, State.Draft):
             if e.button() == Qt.RightButton:
                 n = self.game.remainingTroops
             self.drafted.emit(self.currentTerritory.name, n)
         elif self.game.state in (State.Attack, State.Fortify) and self.game.yourTurn():
             if not self.source and self.currentTerritory.owner == self.game.clientPlayer:
                 self.cancelSelection()
                 self.source = self.currentTerritory
                 mask = self.coloredMask(self.currentTerritory, QColor(170, 170, 0, 200))
                 self.sourceAnimation = BlinkingAnimation(mask, 1000)
                 self.sourceAnimation.updated.connect(self.update)
                 self.sourceAnimation.finished.connect(self.removeAnimation)
                 self.animations.append(self.sourceAnimation)
                 self.sourceAnimation.start()
             elif self.source:
                 target = self.currentTerritory
                 if self.game.state == State.Attack and target.owner != self.source.owner:
                     if e.button() == Qt.RightButton:
                         n = self.source.troopCount - 1
                     self.attacked.emit(self.source.name, target.name, n)
                 elif self.game.state == State.Fortify and target.owner == self.source.owner:
                     if e.button() == Qt.RightButton:
                         n = self.source.troopCount - 1
                     else:
                         (n, ok) = QInputDialog.getInt(self, "Fortify", "Number of troops to move", 1, 1, self.source.troopCount - 1)
                         if not ok:
                             return
                     self.fortified.emit(self.source.name, target.name, n)
                 elif target.owner == self.source.owner:
                     self.cancelSelection()
                     self.source = self.currentTerritory
                     mask = self.coloredMask(self.currentTerritory, QColor(170, 170, 0, 200))
                     self.sourceAnimation = BlinkingAnimation(mask, 1000)
                     self.sourceAnimation.updated.connect(self.update)
                     self.sourceAnimation.finished.connect(self.removeAnimation)
                     self.animations.append(self.sourceAnimation)
                     self.sourceAnimation.start()
     else:
         self.cancelSelection()
Esempio n. 2
0
class BoardWidget(QWidget):
    territoryClaimed = pyqtSignal(str)
    drafted = pyqtSignal(str, int)
    attacked = pyqtSignal(str, str, int)
    fortified = pyqtSignal(str, str, int)

    def __init__(self, game, parent = None):
        QWidget.__init__(self, parent)
        self.game = game
        self.currentTerritory = None
        self.coloredMaskCache = ColoredMaskCache()
        self.animations = []
        self.source = None
        self.sourceAnimation = None
        self.scaleFactor = 1.0
        self.showRegionMap = False
        policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        policy.setHeightForWidth(True)
        self.setSizePolicy(policy)
        self.setMouseTracking(True)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setEnabled(False)
        self.loadImages()
        toggleRegionMap = QAction(self,
                                  shortcutContext=Qt.ApplicationShortcut,
                                  shortcut=Qt.Key_B,
                                  triggered=self.toggleShowRegionMap)
        cancelSelection = QAction(self,
                                  shortcutContext=Qt.ApplicationShortcut,
                                  shortcut=Qt.Key_Escape,
                                  triggered=self.cancelSelection)
        self.addAction(toggleRegionMap)
        self.addAction(cancelSelection)

    def imageRect(self):
        return QRect(QPoint(0, 0), self.imageSize())

    def imageSize(self):
        return self.game.board.image.size() / self.scaleFactor

    def loadImages(self):
        self.game.board.image = QImage(self.game.board.image)
        names = self.game.board.territoryNames()
        progress = QProgressDialog("Loading Board", "", 0, len(names))
        progress.setCancelButton(None)
        progress.setMinimumDuration(0)
        for (i, name) in enumerate(names):
            progress.setValue(i)
            t = self.game.board.getTerritory(name)
            t.image = QImage(t.image)
        #generate region map
        regionOverlay = QImage(self.imageSize(), QImage.Format_ARGB32_Premultiplied)
        regionOverlay.fill(0)
        painter = QPainter()
        painter.begin(regionOverlay)
        labels = []
        for r in self.game.board.regions:
            regionMask = QImage(self.imageSize(), QImage.Format_ARGB32_Premultiplied)
            regionMask.fill(0)
            p = QPainter()
            p.begin(regionMask)
            center = QPoint(0, 0)
            for t in r.territories:
                p.drawImage(0, 0, t.image)
                center += QPoint(*t.center)
            center /= len(r.territories)
            p.end()
            regionImage = QImage(self.imageSize(), QImage.Format_ARGB32_Premultiplied)
            regionImage.fill(0)
            p.begin(regionImage)
            p.setCompositionMode(QPainter.CompositionMode_Source)
            color = [randint(0, 255) for i in range(3)] + [200]
            p.fillRect(regionImage.rect(), QColor(*color))
            p.setCompositionMode(QPainter.CompositionMode_DestinationIn)
            p.drawImage(0, 0, regionMask)
            p.end()
            painter.drawImage(0, 0, regionImage)
            text = "%s: %d" % (r.name, r.bonus)
            labels.append((center, text))

        for l in labels:
            (center, text) = l
            height = painter.fontMetrics().height() + 8
            width = painter.fontMetrics().width(text) + 8
            painter.setPen(Qt.white)
            painter.setBrush(QColor(0, 0, 0, 200))
            textRect = QRect(0, 0, width, height)
            textRect.moveCenter(center)
            painter.drawRect(textRect)
            painter.drawText(textRect, Qt.AlignCenter, text)
        painter.end()
        regionMap = self.game.board.image.copy()
        painter.begin(regionMap)
        painter.drawImage(0, 0, regionOverlay)
        painter.end()
        self.regionMap = QPixmap.fromImage(regionMap)
        self.scaledRegionMap = self.regionMap
        self.ownershipMap = QPixmap.fromImage(self.game.board.image)
        self.scaledOwnershipMap = self.ownershipMap
        troopCountMap = QImage(self.game.board.image.size(), QImage.Format_ARGB32_Premultiplied)
        troopCountMap.fill(0)
        self.troopCountMap = QPixmap.fromImage(troopCountMap)
        self.scaledTroopCountMap = self.troopCountMap

    def recreateMasks(self):
        self.masks = {}
        for t in self.game.board.iterTerritories():
            self.masks[t] = t.image.scaled(self.imageSize())
        self.coloredMaskCache.clear()

    def updateTerritoryOwner(self, name, owner):
        t = self.game.board.getTerritory(name)
        p = self.game.getPlayer(owner)
        color = QColor(*p.color)
        color.setAlpha(200)
        territoryImage = QImage(t.image.size(), QImage.Format_ARGB32_Premultiplied)
        p = QPainter()
        p.begin(territoryImage)
        p.drawImage(0, 0, self.game.board.image)
        p.setCompositionMode(QPainter.CompositionMode_DestinationIn)
        p.drawImage(0, 0, t.image)
        p.end()
        coloredTerritoryImage = QImage(territoryImage.size(), QImage.Format_ARGB32_Premultiplied)
        coloredTerritoryImage.fill(0)
        p.begin(coloredTerritoryImage)
        p.fillRect(territoryImage.rect(), color)
        p.setCompositionMode(QPainter.CompositionMode_DestinationIn)
        p.drawImage(0, 0, t.image)
        p.end()
        p.begin(self.ownershipMap)
        p.drawImage(0, 0, territoryImage)
        p.drawImage(0, 0, coloredTerritoryImage)
        p.end()
        self.scaledOwnershipMap = self.ownershipMap.scaled(self.imageSize())
        self.update()

    def updateTerritoryTroopCount(self, name, count):
        t = self.game.board.getTerritory(name)
        x = t.center[0] - 12
        y = t.center[1] - 12
        width = 25
        height = 25
        painter = QPainter()
        painter.begin(self.troopCountMap)
        painter.setPen(Qt.white)
        painter.setBrush(Qt.black)
        painter.drawEllipse(x, y, width, height)
        painter.drawText(x, y, width, height, Qt.AlignCenter, str(t.troopCount))
        painter.end()
        self.scaledTroopCountMap = self.troopCountMap.scaled(self.imageSize())
        self.update()

    def toggleShowRegionMap(self):
        self.showRegionMap = not self.showRegionMap
        self.update()

    def cancelSelection(self):
        self.source = None
        if self.sourceAnimation:
            self.sourceAnimation.finished.emit()
            self.sourceAnimation = None
            self.update()

    def territoryAt(self, x, y):
        for t in self.game.board.iterTerritories():
            if t.image.pixel(x, y):
                return t

    def removeAnimation(self):
        anim = self.sender()
        self.animations.remove(anim)
        anim.deleteLater()

    def attack(self, attacker, source, target):
        source = self.game.board.getTerritory(source)
        target = self.game.board.getTerritory(target)
        attacker = self.game.getPlayer(attacker)
        anim = ExplodingAnimation(QPoint(*target.center) / self.scaleFactor, 1000)
        anim.finished.connect(self.removeAnimation)
        anim.updated.connect(self.repaint)
        self.animations.append(anim)
        anim.start()

    def stateChange(self, old, new):
        self.cancelSelection()

    def sizeHint(self):
        return self.game.board.image.size()

    def heightForWidth(self, width):
        aspect = float(self.sizeHint().width()) / self.sizeHint().height()
        return int(width / aspect)

    def resizeEvent(self, event):
        self.cancelSelection()
        size = self.sizeHint()
        size.scale(event.size(), Qt.KeepAspectRatio)
        self.scaleFactor = float(self.sizeHint().width()) / size.width()
        self.recreateMasks()
        self.scaledRegionMap = self.regionMap.scaled(size)
        self.scaledOwnershipMap = self.ownershipMap.scaled(size)
        self.scaledTroopCountMap = self.troopCountMap.scaled(size)
        self.coloredMaskCache.clear()
        self.update()

    def mouseMoveEvent(self, event):
        oldTerritory = self.currentTerritory
        if self.imageRect().contains(event.pos()):
            x = event.pos().x() * self.scaleFactor
            y = event.pos().y() * self.scaleFactor
            self.currentTerritory = self.territoryAt(x, y)
        else:
            self.currentTerritory = None
        if self.currentTerritory:
            self.setToolTip(self.currentTerritory.name)
        else:
            self.setToolTip("")
        if oldTerritory != self.currentTerritory:
            self.update()

    def mouseReleaseEvent(self, e):
        if self.currentTerritory:
            if self.game.state == State.InitialPlacement:
                self.territoryClaimed.emit(self.currentTerritory.name)
                return
            m = e.modifiers()
            if m & Qt.ShiftModifier:
                n = 3
            elif m & Qt.AltModifier:
                n = 10
            else:
                n = 1
            if self.game.state in (State.InitialDraft, State.Draft):
                if e.button() == Qt.RightButton:
                    n = self.game.remainingTroops
                self.drafted.emit(self.currentTerritory.name, n)
            elif self.game.state in (State.Attack, State.Fortify) and self.game.yourTurn():
                if not self.source and self.currentTerritory.owner == self.game.clientPlayer:
                    self.cancelSelection()
                    self.source = self.currentTerritory
                    mask = self.coloredMask(self.currentTerritory, QColor(170, 170, 0, 200))
                    self.sourceAnimation = BlinkingAnimation(mask, 1000)
                    self.sourceAnimation.updated.connect(self.update)
                    self.sourceAnimation.finished.connect(self.removeAnimation)
                    self.animations.append(self.sourceAnimation)
                    self.sourceAnimation.start()
                elif self.source:
                    target = self.currentTerritory
                    if self.game.state == State.Attack and target.owner != self.source.owner:
                        if e.button() == Qt.RightButton:
                            n = self.source.troopCount - 1
                        self.attacked.emit(self.source.name, target.name, n)
                    elif self.game.state == State.Fortify and target.owner == self.source.owner:
                        if e.button() == Qt.RightButton:
                            n = self.source.troopCount - 1
                        else:
                            (n, ok) = QInputDialog.getInt(self, "Fortify", "Number of troops to move", 1, 1, self.source.troopCount - 1)
                            if not ok:
                                return
                        self.fortified.emit(self.source.name, target.name, n)
                    elif target.owner == self.source.owner:
                        self.cancelSelection()
                        self.source = self.currentTerritory
                        mask = self.coloredMask(self.currentTerritory, QColor(170, 170, 0, 200))
                        self.sourceAnimation = BlinkingAnimation(mask, 1000)
                        self.sourceAnimation.updated.connect(self.update)
                        self.sourceAnimation.finished.connect(self.removeAnimation)
                        self.animations.append(self.sourceAnimation)
                        self.sourceAnimation.start()
        else:
            self.cancelSelection()
                        

    def coloredMask(self, territory, color):
        pixmap = self.coloredMaskCache.get(territory, color)
        if not pixmap:
            mask = self.masks[territory]
            size = self.imageSize()
            rect = self.imageRect()
            image = QImage(size, QImage.Format_ARGB32_Premultiplied)
            image.fill(0)
            painter = QPainter()
            painter.begin(image)
            painter.setCompositionMode(QPainter.CompositionMode_Source)
            color.setAlpha(100)
            painter.fillRect(rect, color)
            painter.setCompositionMode(QPainter.CompositionMode_DestinationIn)
            painter.drawImage(0, 0, mask)
            painter.end()
            pixmap = QPixmap.fromImage(image)
            self.coloredMaskCache.set(territory, color, pixmap)
        return pixmap

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        if not self.showRegionMap:
            painter.drawPixmap(0, 0, self.scaledOwnershipMap)
        rect = self.imageRect()
        if self.isEnabled():
            if self.showRegionMap:
                painter.drawPixmap(0, 0, self.scaledRegionMap)
            else:
                if self.currentTerritory:
                    painter.drawPixmap(0, 0, self.coloredMask(self.currentTerritory, QColor(*self.game.clientPlayer.color)))
                #draw active animations
                for a in self.animations:
                    painter.save()
                    a.paint(painter)
                    painter.restore()
                painter.drawPixmap(0, 0, self.scaledTroopCountMap)
        else:
            painter.fillRect(rect, QColor(0, 0, 0, 200))
            painter.drawText(rect, Qt.AlignCenter, "Waiting for the game to start.")

        #remaining troops
        if self.game.yourTurn() and self.game.remainingTroops:
            troopText = "Remaining troops: %d" % self.game.remainingTroops
            troopRect = QRect(0, 0, painter.fontMetrics().width(troopText) + 8, painter.fontMetrics().height() + 8)
            troopRect.moveBottomLeft(rect.bottomLeft())
            painter.setPen(Qt.white)
            painter.setBrush(QColor(0, 0, 0, 200))
            painter.drawRect(troopRect)
            painter.drawText(troopRect, Qt.AlignCenter, troopText)

        painter.end()