File indexing completed on 2024-04-21 04:01:56

0001 # -*- coding: utf-8 -*-
0002 
0003 """
0004 Copyright (C) 2013-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de>
0005 
0006 SPDX-License-Identifier: GPL-2.0
0007 
0008 """
0009 
0010 from qt import Qt
0011 
0012 from mi18n import i18nc
0013 from message import Message
0014 from common import Internal, isAlive
0015 from player import Player, PlayingPlayer
0016 from game import PlayingGame
0017 from tile import Tile
0018 from handboard import PlayingHandBoard
0019 from animation import AnimationSpeed
0020 from uiwall import UIWall, SideText
0021 from wind import Wind
0022 
0023 
0024 class VisiblePlayer(Player):
0025 
0026     """Mixin for VisiblePlayingPlayer and ScoringPlayer"""
0027 
0028     def __init__(self):
0029         # pylint: disable=super-init-not-called
0030         self.__front = self.game.wall[self.idx]
0031         self.sideText = SideText()
0032         self.sideText.board = self.__front
0033 
0034     def hide(self):
0035         """clear visible data and hide"""
0036         self.clearHand()
0037         self.handBoard.hide()
0038 
0039     @property
0040     def idx(self):
0041         """our index in the player list"""
0042         if self not in self.game.players:
0043             # we will be added next
0044             return len(self.game.players)
0045         return self.game.players.index(self)
0046 
0047     @property
0048     def front(self):
0049         """front"""
0050         return self.__front
0051 
0052     @front.setter
0053     def front(self, value):
0054         """also assign handBoard to front"""
0055         self.__front = value
0056         if value and self.handBoard:
0057             self.handBoard.setParentItem(value)
0058 
0059     def syncHandBoard(self, adding=None):
0060         """update display of handBoard. Set Focus to tileName."""
0061         self.handBoard.sync(adding)
0062 
0063     def showInfo(self):
0064         """show player info on the wall"""
0065         side = self.front
0066         self.sideText.text = '{} - {}'.format(self.localName, self.explainHand().total())
0067         self.colorizeName()
0068         side.windTile = Wind.all4[self.wind].marker
0069         side.windTile.prevailing = self.game.roundsFinished
0070         side.windTile.board = self.front
0071 
0072 
0073 class VisiblePlayingPlayer(VisiblePlayer, PlayingPlayer):
0074 
0075     """this player instance has a visual representation"""
0076     # pylint: disable=too-many-public-methods
0077 
0078     def __init__(self, game, name):
0079         assert game
0080         self.handBoard = None  # because Player.init calls clearHand()
0081         PlayingPlayer.__init__(self, game, name)
0082         VisiblePlayer.__init__(self)
0083         self.handBoard = PlayingHandBoard(self)
0084         self.voice = None
0085 
0086     def clearHand(self):
0087         """clears attributes related to current hand"""
0088         super().clearHand()
0089         if self.game and self.game.wall:
0090             # is None while __del__
0091             self.front = self.game.wall[self.idx]
0092         if self.handBoard:
0093             self.handBoard.setEnabled(
0094                 self.game and self.game.belongsToHumanPlayer(
0095                 ) and self == self.game.myself)
0096 
0097     def explainHand(self):
0098         """return the hand to be explained. Same as current unless we need to discard.
0099         In that case, make an educated guess about the discard.
0100         For player==game.myself, use the focused tile."""
0101         hand = self.hand
0102         if hand and hand.tiles and self._concealedTiles:
0103             if hand.lenOffset == 1 and not hand.won:
0104                 if any(not x.isKnown for x in self._concealedTiles):
0105                     hand -= Tile.unknown
0106                 elif self.handBoard.focusTile:
0107                     hand -= self.handBoard.focusTile.tile
0108         return hand
0109 
0110     def colorizeName(self):
0111         """set the color to be used for showing the player name on the wall"""
0112         if not isAlive(self.sideText):
0113             return
0114         if self == self.game.activePlayer and self.game.client:
0115             color = Qt.blue
0116         elif Internal.Preferences.tilesetName == 'jade':
0117             color = Qt.white
0118         else:
0119             color = Qt.black
0120         self.sideText.color = color
0121 
0122     def getsFocus(self, unusedResults=None):
0123         """give this player focus on his handBoard"""
0124         self.handBoard.setEnabled(True)
0125         self.handBoard.hasLogicalFocus = True
0126 
0127     def popupMsg(self, msg):
0128         """shows a yellow message from player"""
0129         if msg != Message.NoClaim:
0130             self.speak(msg.name.lower())
0131             yellow = self.front.message
0132             yellow.setText('{}  {}'.format(yellow.msg, i18nc('kajongg', msg.name)))
0133             yellow.setVisible(True)
0134 
0135     def hidePopup(self):
0136         """hide the yellow message from player"""
0137         if isAlive(self.front.message):
0138             self.front.message.msg = ''
0139             self.front.message.setVisible(False)
0140 
0141     def speak(self, txt):
0142         """speak if we have a voice"""
0143         if self.voice:
0144             self.voice.speak(txt, self.front.rotation())
0145 
0146     def robTileFrom(self, tile):
0147         """used for robbing the kong from this player"""
0148         PlayingPlayer.robTileFrom(self, tile)
0149         tile = tile.exposed
0150         hbTiles = self.handBoard.uiTiles
0151         lastDiscard = [x for x in hbTiles if x.tile == tile][-1]
0152         lastDiscard.tile = lastDiscard.tile.concealed
0153         Internal.scene.discardBoard.lastDiscarded = lastDiscard
0154         # remove from board of robbed player, otherwise syncHandBoard would
0155         # not fix display for the robbed player
0156         lastDiscard.setBoard(None)
0157         assert lastDiscard.tile.isConcealed
0158         self.syncHandBoard()
0159 
0160     def addConcealedTiles(self, tiles, animated=True):
0161         """add to my tiles and sync the hand board"""
0162         with AnimationSpeed(speed=Internal.Preferences.animationSpeed if animated else 99):
0163             PlayingPlayer.addConcealedTiles(self, [x.tile for x in tiles])
0164             self.syncHandBoard(tiles)
0165 
0166     def declaredMahJongg(self, concealed, withDiscard, lastTile, lastMeld):
0167         """player declared mah jongg. Determine last meld, show
0168         concealed tiles grouped to melds"""
0169         PlayingPlayer.declaredMahJongg(
0170             self,
0171             concealed,
0172             withDiscard,
0173             lastTile,
0174             lastMeld)
0175         if withDiscard:
0176             # withDiscard is a Tile, we need the UITile
0177             discardTile = Internal.scene.discardBoard.lastDiscarded
0178             if discardTile.tile is not withDiscard:
0179                 self.game.debug(
0180                     '%s is not %s' %
0181                     (discardTile.tile, withDiscard))
0182                 assert False
0183             self.syncHandBoard([discardTile])
0184         else:
0185             # show concealed tiles
0186             self.syncHandBoard()
0187 
0188     def removeTile(self, tile):
0189         """remove from my melds or tiles"""
0190         PlayingPlayer.removeTile(self, tile)
0191         self.syncHandBoard()
0192 
0193     def makeTileKnown(self, tile):
0194         """give an unknown tileItem a name"""
0195         PlayingPlayer.makeTileKnown(self, tile)
0196         assert tile.isKnown
0197         matchingTiles = sorted(
0198             self.handBoard.tilesByElement(Tile.unknown),
0199             key=lambda x: x.xoffset)
0200         matchingTiles[-1].tile = tile
0201 
0202     def exposeMeld(self, meldTiles, calledTile=None):
0203         result = PlayingPlayer.exposeMeld(
0204             self,
0205             meldTiles,
0206             calledTile.tile if calledTile else None)
0207         adding = [calledTile] if calledTile else None
0208         self.syncHandBoard(adding=adding)
0209         return result
0210 
0211 
0212 class VisiblePlayingGame(PlayingGame):
0213 
0214     """for the client"""
0215     # pylint: disable=too-many-arguments, too-many-public-methods
0216     playerClass = VisiblePlayingPlayer
0217     wallClass = UIWall
0218 
0219     def __init__(self, names, ruleset, gameid=None,
0220                  wantedGame=None, client=None, playOpen=False, autoPlay=False):
0221         PlayingGame.__init__(
0222             self, names, ruleset, gameid, wantedGame=wantedGame,
0223             client=client, playOpen=playOpen, autoPlay=autoPlay)
0224 #        Internal.mainWindow.adjustMainView()
0225 #        Internal.mainWindow.updateGUI()
0226         self.wall.decorate4()
0227 
0228     def close(self):
0229         """close the game"""
0230         scene = Internal.scene
0231         scene.discardBoard.hide()
0232         if isAlive(scene):
0233             scene.removeTiles()
0234         scene.clientDialog = None
0235         for player in self.players:
0236             player.hide()
0237         if self.wall:
0238             self.wall.hide()
0239         if isAlive(scene.mainWindow):
0240             scene.mainWindow.actionAutoPlay.setChecked(False)
0241         scene.startingGame = False
0242         scene.game = None
0243         SideText.removeAll()
0244         scene.mainWindow.updateGUI()
0245         return PlayingGame.close(self)