class RadioButtonGroup(framework.CompoundElement):
    def __init__(self, app):
        super(RadioButtonGroup, self).__init__(app)
        self.elements = []
        self.selectedIndex = -1
        self.onSelectionChanged = Event()
        self.by_value = {}

    def add(self, radioButton):
        self.elements.append(radioButton)
        self.by_value[radioButton.value] = radioButton
        radioButton._joinGroup(self)

    def getSelectedValue(self):
        if self.selectedIndex == -1:
            return None
        else:
            return self.elements[self.selectedIndex].value

    def selected(self, radioButton):
        if radioButton in self.by_value:
            radioButton = self.by_value[radioButton]
        assert radioButton in self.elements, ("Cannot select a radiobutton "
                                              "that isn't in this group")

        newIndex = self.elements.index(radioButton)
        if newIndex == self.selectedIndex:
            return

        if self.selectedIndex != -1:
            self.elements[self.selectedIndex].deselected()
        self.selectedIndex = newIndex
        radioButton.selected()
        self.onSelectionChanged.execute(self.selectedIndex)
Exemplo n.º 2
0
class TextBoxCell(Cell):
    def __init__(self, app, row, column):
        super(TextBoxCell, self).__init__(app, row, column)
        textAlign = self.styleGet('textAlign')
        self.inputBox = InputBox(
            self.app,
            Area(
                CellAttachedPoint((0, 0), self, textAlign),
                self._getUsableRect().size, textAlign),
            font=self.styleGet('font'),
            colour=self.styleGet('foreColour'))
        self.inputBox.onEdit.addListener(self._valueChanged)
        self.elements = [self.inputBox]
        self._oldText = ''
        self._readOnly = True
        self.setReadOnly(False)
        self.onValueChanged = Event()

    def _valueChanged(self, sender):
        self._oldText = self.inputBox.getValue()
        self.onValueChanged.execute(self)

    def setReadOnly(self, readOnly):
        if readOnly == self._readOnly:
            return
        self._readOnly = readOnly

        if readOnly:
            self.inputBox.onClick.removeListener(self._boxClicked)
        else:
            self.inputBox.onClick.addListener(self._boxClicked)
            self.setFocus(None)

    def lostFocus(self):
        self.setFocus(None)

    def setText(self, text):
        if text != self._oldText:
            self.inputBox.setValue(text)
            self._styleChanged = True
            self._oldText = text

    def getText(self):
        return self._oldText

    def setMaxLength(self, length):
        self.inputBox.setMaxLength(length)

    def setValidator(self, validator):
        self.inputBox.setValidator(validator)

    def _update(self):
        self.inputBox.setFont(self.styleGet('font'))
        self.inputBox.setFontColour(self.styleGet('foreColour'))
        bgColour = self.styleGet('backColour')
        if bgColour is not None:
            self.inputBox.setBackColour(bgColour)
        textAlign = self.styleGet('textAlign')
        self.inputBox.area.anchor = textAlign
        self.inputBox.area.point.attachedAt = textAlign
Exemplo n.º 3
0
class Hotkey(Element):
    def __init__(self, app, key, modifiers):
        super(Hotkey, self).__init__(app)
        self.onTriggered = Event()
        self.key = key
        self.mods = modifiers

    def processEvent(self, event):
        if event.type == pygame.KEYDOWN:
            if self.testEquals(event.key, event.mod):
                self.onTriggered.execute()
                return
        return event

    def __str__(self):
        return keyboard.shortcutName(self.key, self.mods)

    def testEquals(self, key, modifiers, mapping=None):
        'Checks if the key press matches this shortcut.'

        # First test: key equality.
        if key <> self.key:
            return False

        # Second test: modifier compatibility.
        for kmod, modName in keyboard.KMOD_NAMES:
            if self.mods & kmod:
                if not (modifiers & kmod):
                    return False
            elif modifiers & kmod:
                return False
        # Testing modifiers as done above ignores left/right status.

        return True
Exemplo n.º 4
0
class MenuManager(object):
    def __init__(self, defaultMenu=None):
        self.defaultMenu = defaultMenu
        self.menu = defaultMenu
        self.menuStack = []
        self.onShowMenu = Event()  # (menu)

    def setDefaultMenu(self, defaultMenu):
        default = len(self.menuStack) == 0 and self.menu is self.defaultMenu
        self.defaultMenu = defaultMenu

        if default or self.menu is None:
            self.menu = defaultMenu
            self.onShowMenu.execute(defaultMenu)

    def reset(self):
        self.menu = self.defaultMenu
        self.menuStack = []

        self.onShowMenu.execute(self.defaultMenu)

    def cancel(self):
        if len(self.menuStack) > 0:
            self.menu = self.menuStack.pop()
        else:
            self.menu = self.defaultMenu

        self.onShowMenu.execute(self.menu)

    def showMenu(self, menu):
        self.menuStack.append(self.menu)
        self.menu = menu

        self.onShowMenu.execute(menu)
Exemplo n.º 5
0
class ConnectionFailedDialog(DialogBox):
    def __init__(self, app, reason):
        width = app.screenManager.scaleFactor * absoluteWidth

        font = app.fonts.connectionFailedFont
        boundary = app.screenManager.scaleFactor * 20
        lines = wrapWords(app, reason, font, width - 2 * boundary)

        x = boundary
        elements = []
        colours = app.theme.colours
        for text in lines:
            elements.append(
                TextElement(app,
                            text,
                            font,
                            Location(
                                DialogBoxAttachedPoint(self, (0, x), 'midtop'),
                                'midtop'),
                            colour=colours.errorColour))
            x += font.getHeight(app)

        # Boundary * 3 for top, bottom, and middle
        height = boundary * 3 + font.getHeight(app) * (len(lines) + 1)
        size = ScaledSize(absoluteWidth, height)
        super(ConnectionFailedDialog, self).__init__(app, size,
                                                     'Connection Failed')

        self.elements = elements
        self.elements.append(
            TextButton(app,
                       Location(
                           DialogBoxAttachedPoint(self, (0, -boundary),
                                                  'midbottom'), 'midbottom'),
                       'OK',
                       font,
                       colours.dialogButtonColour,
                       colours.radioMouseover,
                       onClick=lambda sender: self.close()))
        self.onEnter = Event()
        self.onEnter.addListener(self.close)

    def processEvent(self, event):
        if event.type == pygame.KEYDOWN and event.key in (pygame.K_KP_ENTER,
                                                          pygame.K_RETURN):
            self.onEnter.execute()
            return None
        else:
            return super(ConnectionFailedDialog, self).processEvent(event)
Exemplo n.º 6
0
class MenuItem(object):
    '''
    @param name     the caption of the menu item
    @param action   a callable for when the item is selected
    '''
    def __init__(self, name, action=None, listener=None, enabled=True):
        self.name = name
        self.onClick = Event()  # (MenuItem)
        self.onAction = Event()  # (action)
        self.action = action
        self.enabled = enabled
        if listener is not None:
            self.onClick.addListener(listener)

    def execute(self):
        self.onClick.execute(self)
        if self.action is not None:
            self.onAction.execute(self.action)
Exemplo n.º 7
0
class PractiseScreen(framework.CompoundElement):
    def __init__(self, app, onClose=None, onStart=None):
        super(PractiseScreen, self).__init__(app)

        self.onClose = Event()
        if onClose is not None:
            self.onClose.addListener(onClose)
        self.onStart = Event()
        if onStart is not None:
            self.onStart.addListener(onStart)

        if app.displaySettings.alphaOverlays:
            alpha = 192
        else:
            alpha = None
        bg = SolidRect(app, app.theme.colours.playMenu, alpha,
                       Region(centre=Canvas(512, 384), size=Canvas(924, 500)))

        #colour = app.theme.colours.mainMenuColour
        #font = app.screenManager.fonts.consoleFont

        font = app.screenManager.fonts.bigMenuFont
        cancel = TextButton(app,
                            Location(Canvas(512, 624), 'midbottom'),
                            'Cancel',
                            font,
                            app.theme.colours.secondMenuColour,
                            app.theme.colours.white,
                            onClick=self.cancel)
        self.elements = [bg, cancel]

    def cancel(self, element):
        self.onClose.execute()

    def startGame(self):
        db = self.app.layoutDatabase
        game = LocalGame(db, SIZE[0], SIZE[1], onceOnly=True)

        for i in range(AICOUNT):
            game.addBot('ranger')

        self.onStart.execute(game)
Exemplo n.º 8
0
class MappingHotkey(Element):
    def __init__(self, app, key, mapping=None):
        '''
        key may be a physical pygame key or a string where
            mapping[pygameKey] = key
        '''
        super(MappingHotkey, self).__init__(app)
        self.key = key
        self.onActivated = Event()

        if mapping is None:
            mapping = {}
        self.mapping = mapping

    def processEvent(self, event):
        if event.type == pygame.KEYDOWN:
            if self.mapping.get(event.key, None) == self.key:
                self.onActivated.execute()
                return None
            if event.key == self.key:
                self.onActivated.execute()
                return None
        return event

    def __repr__(self):
        if isinstance(self.key, int):
            key = self.key
        else:
            # Calculate the physical key.
            for key, value in self.mapping.iteritems():
                if value == self.key:
                    break
            else:
                return '??'

        return keyboard.shortcutName(key, 0)
Exemplo n.º 9
0
class Hotkeys(Element):
    '''
    Traps any keys which occur in the mapping and carries out some action on
    them. Note that this observes only, but does not stop the keypress events
    from passing through.
    '''
    def __init__(self, app, mapping, onActivated=None):
        super(Hotkeys, self).__init__(app)
        self.mapping = mapping
        self.onActivated = Event()
        if onActivated is not None:
            self.onActivated.addListener(onActivated)

    def processEvent(self, event):
        if event.type == pygame.KEYDOWN:
            try:
                action = self.mapping[event.key]
            except KeyError:
                return event

            # Process this hotkey.
            self.onActivated.execute(action)
        return event

    def __repr__(self):
        if isinstance(self.key, int):
            key = self.key
        else:
            # Calculate the physical key.
            for key, value in self.mapping.iteritems():
                if value == self.key:
                    break
            else:
                return '??'

        return keyboard.shortcutName(key, 0)
Exemplo n.º 10
0
class GameInterface(framework.CompoundElement, ConcreteAgent):
    '''Interface for when we are connected to a game.'''

    achievementDefs = availableAchievements

    def __init__(self,
                 app,
                 game,
                 onDisconnectRequest=None,
                 onConnectionLost=None,
                 replay=False,
                 authTag=0):
        super(GameInterface, self).__init__(app, game=game)
        self.localState.onShoxwave.addListener(self.localShoxwaveFired)
        self.localState.onGameInfoChanged.addListener(self.gameInfoChanged)
        self.world.onOpenChatReceived.addListener(self.openChat)
        self.world.onTeamChatReceived.addListener(self.teamChat)
        self.world.onReset.addListener(self.worldReset)
        self.world.onGrenadeExplosion.addListener(self.grenadeExploded)
        self.world.onTrosballExplosion.addListener(self.trosballExploded)
        self.world.onBomberExplosion.addListener(self.trosballExploded)
        self.world.uiOptions.onChange.addListener(self.uiOptionsChanged)

        self.authTag = authTag

        self.subscribedPlayers = set()

        self.onDisconnectRequest = Event()
        if onDisconnectRequest is not None:
            self.onDisconnectRequest.addListener(onDisconnectRequest)

        self.onConnectionLost = Event()
        if onConnectionLost is not None:
            self.onConnectionLost.addListener(onConnectionLost)
        self.game = game

        self.joinDialogReason = None

        self.keyMapping = keyboard.KeyboardMapping(keymap.default_game_keys)
        self.runningPlayerInterface = None
        self.updateKeyMapping()
        self.gameViewer = viewManager.GameViewer(self.app, self, game, replay)
        if replay:
            self.joinController = None
        else:
            self.joinController = JoinGameController(self.app, self, self.game)
        self.detailsInterface = DetailsInterface(self.app, self)
        self.winnerMsg = WinnerMsg(app)
        self.timingInfo = TimingInfo(
            app,
            self,
            Location(Canvas(307, 768), 'midbottom'),
            app.screenManager.fonts.consoleFont,
        )
        self.gameInfoDisplay = GameInfoDisplay(
            app, self, Region(topleft=Screen(0.01, 0.05),
                              size=Canvas(330, 200)))
        self.hotkeys = hotkey.Hotkeys(self.app, self.keyMapping,
                                      self.detailsInterface.doAction)
        self.terminal = None

        self.joinInProgress = False

        self.vcInterface = None
        if replay:
            self.vcInterface = ViewControlInterface(self.app, self)

        self.ready = False
        defer.maybeDeferred(game.addAgent, self).addCallback(self.addedAgent)

        self.setElements()

    def gameInfoChanged(self):
        self.gameInfoDisplay.refreshInfo()

    def addedAgent(self, result):
        self.ready = True
        if self.joinController:
            self.joinDialogReason = 'automatic'
            self.joinController.start()

    def spectatorWantsToJoin(self):
        if self.runningPlayerInterface or not self.joinController:
            return
        self.joinDialogReason = 'from menu'
        self.joinController.maybeShowJoinDialog(autoJoin=True)

    def sendRequest(self, msg):
        if not self.ready:
            # Not yet completely connected to game
            return
        super(GameInterface, self).sendRequest(msg)

    def worldReset(self, *args, **kwarsg):
        self.winnerMsg.hide()
        if self.ready and self.joinController:
            self.joinController.gotWorldReset()
        self.gameViewer.reset()

    def updateKeyMapping(self):
        # Set up the keyboard mapping.
        try:
            # Try to load keyboard mappings from the user's personal settings.
            config = open(getPath(user, 'keymap'), 'rU').read()
            self.keyMapping.load(config)
            if self.runningPlayerInterface:
                self.runningPlayerInterface.keyMappingUpdated()
        except IOError:
            pass

    @ConnectionLostMsg.handler
    def connectionLost(self, msg):
        self.cleanUp()
        if self.joinController:
            self.joinController.hide()
        self.onConnectionLost.execute()

    def joined(self, player):
        '''Called when joining of game is successful.'''
        pygame.key.set_repeat()
        self.gameViewer.worldgui.overridePlayer(self.localState.player)
        self.runningPlayerInterface = pi = PlayerInterface(self.app, self)
        self.detailsInterface.setPlayer(pi.player)
        self.setElements()

        self.joinController.hide()
        self.gameViewer.leaderboard.update()

    def spectate(self):
        '''
        Called by join controller if user selects to only spectate.
        '''
        self.vcInterface = ViewControlInterface(self.app, self)
        self.setElements()
        self.joinController.hide()

    def joinDialogCancelled(self):
        if self.joinDialogReason == 'automatic':
            self.disconnect()
        else:
            self.spectate()

    def stop(self):
        super(GameInterface, self).stop()
        self.localState.onShoxwave.removeListener(self.localShoxwaveFired)
        self.localState.onGameInfoChanged.removeListener(self.gameInfoChanged)
        self.world.onOpenChatReceived.removeListener(self.openChat)
        self.world.onTeamChatReceived.removeListener(self.teamChat)
        self.world.onReset.removeListener(self.worldReset)
        self.world.onGrenadeExplosion.removeListener(self.grenadeExploded)
        self.world.onTrosballExplosion.removeListener(self.trosballExploded)
        self.world.onBomberExplosion.removeListener(self.trosballExploded)
        self.world.uiOptions.onChange.removeListener(self.uiOptionsChanged)
        self.gameViewer.stop()
        if self.runningPlayerInterface is not None:
            self.runningPlayerInterface.stop()

    def setElements(self):
        spectate = replay = False
        if self.runningPlayerInterface:
            self.elements = [
                self.gameViewer, self.runningPlayerInterface,
                self.gameInfoDisplay, self.hotkeys, self.detailsInterface,
                self.winnerMsg, self.timingInfo
            ]
        else:
            self.elements = [
                self.gameViewer, self.gameInfoDisplay, self.hotkeys,
                self.detailsInterface, self.winnerMsg, self.timingInfo
            ]
            if self.vcInterface is not None:
                self.elements.insert(2, self.vcInterface)

            if self.joinController:
                spectate = True
            else:
                replay = True
        self.detailsInterface.menuManager.setMode(spectate=spectate,
                                                  replay=replay)

    def toggleTerminal(self):
        if self.terminal is None:
            locs = {'app': self.app}
            if hasattr(self.app, 'getConsoleLocals'):
                locs.update(self.app.getConsoleLocals())
            self.terminal = console.TrosnothInteractiveConsole(
                self.app,
                self.app.screenManager.fonts.consoleFont,
                Region(size=Screen(1, 0.4), bottomright=Screen(1, 1)),
                locals=locs)
            self.terminal.interact().addCallback(self._terminalQuit)

        from trosnoth.utils.utils import timeNow
        if self.terminal in self.elements:
            if timeNow() > self._termWaitTime:
                self.elements.remove(self.terminal)
        else:
            self._termWaitTime = timeNow() + 0.1
            self.elements.append(self.terminal)
            self.setFocus(self.terminal)

    def _terminalQuit(self, result):
        if self.terminal in self.elements:
            self.elements.remove(self.terminal)
        self.terminal = None

    def disconnect(self):
        self.cleanUp()
        self.onDisconnectRequest.execute()

    def joinGame(self, nick, team, timeout=10):
        if self.joinInProgress:
            return

        if team is None:
            teamId = NEUTRAL_TEAM_ID
        else:
            teamId = team.id

        self.joinInProgress = True
        self.sendJoinRequest(teamId, nick, authTag=self.authTag)
        WeakCallLater(timeout, self, '_joinTimedOut')

    def setPlayer(self, player):
        if not player:
            self.gameViewer.worldgui.removeOverride()
            self.lostPlayer()

        super(GameInterface, self).setPlayer(player)

        if player:
            if __debug__ and globaldebug.enabled:
                globaldebug.localPlayerId = player.id

            self.joinInProgress = False
            self.joined(player)

    @CannotJoinMsg.handler
    def joinFailed(self, msg):
        self.joinInProgress = False
        self.joinController.joinFailed(msg.reasonId)

    def _joinTimedOut(self):
        if self.player or not self.joinInProgress:
            return
        self.joinInProgress = False
        self.joinController.joinFailed('timeout')

    def cleanUp(self):
        if self.gameViewer.timerBar is not None:
            self.gameViewer.timerBar = None
        pygame.key.set_repeat(300, 30)

    def uiOptionsChanged(self):
        if self.world.uiOptions.showGameOver:
            winner = self.world.uiOptions.winningTeam
            if winner:
                self.winnerMsg.show(
                    '{} win'.format(winner),
                    self.app.theme.colours.chatColour(winner),
                )
            else:
                self.winnerMsg.show('Game drawn', (128, 128, 128))
        else:
            self.winnerMsg.hide()

    @PlayerCoinsSpentMsg.handler
    def discard(self, msg):
        pass

    @AwardPlayerCoinMsg.handler
    def playerAwardedCoin(self, msg):
        if not self.localState.player:
            return
        if msg.sound and msg.playerId == self.localState.player.id:
            self.playSound('gotCoin')

    @PlayerHasElephantMsg.handler
    def gotElephant(self, msg, _lastElephantPlayer=[None]):
        player = self.world.getPlayer(msg.playerId)
        if player and player != _lastElephantPlayer[0]:
            message = '%s now has Jerakeen!' % (player.nick, )
            self.detailsInterface.newMessage(message)
            _lastElephantPlayer[0] = player

    @PlayerHasTrosballMsg.handler
    def gotTrosball(self, msg, _lastTrosballPlayer=[None]):
        player = self.world.playerWithId.get(msg.playerId)

        if player != _lastTrosballPlayer[0]:
            _lastTrosballPlayer[0] = player
            if player is None:
                message = 'The ball has been dropped!'
            else:
                message = '%s has the ball!' % (player.nick, )
            self.detailsInterface.newMessage(message)

    @AddPlayerMsg.handler
    def addPlayer(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player and player not in self.subscribedPlayers:
            self.subscribedPlayers.add(player)
            team = player.team if player.team else self.world.rogueTeamName
            message = '%s has joined %s' % (player.nick, team)
            self.detailsInterface.newMessage(message)
            player.onDied.addListener(partial(self.playerDied, player))

    @SetPlayerTeamMsg.handler
    def changeTeam(self, msg):
        self.defaultHandler(msg)  # Make sure the local player changes team
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s has joined %s' % (player.nick,
                                            self.world.getTeamName(msg.teamId))
            self.detailsInterface.newMessage(message)

    @RemovePlayerMsg.handler
    def handle_RemovePlayerMsg(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s has left the game' % (player.nick, )
            self.detailsInterface.newMessage(message)
            self.subscribedPlayers.discard(player)

    def lostPlayer(self):
        self.runningPlayerInterface.stop()
        self.runningPlayerInterface = None
        self.setElements()

    @CannotBuyUpgradeMsg.handler
    def notEnoughCoins(self, msg):
        if msg.reasonId == NOT_ENOUGH_COINS_REASON:
            text = 'Your team does not have enough coins.'
        elif msg.reasonId == CANNOT_REACTIVATE_REASON:
            text = 'You already have that item.'
        elif msg.reasonId == PLAYER_DEAD_REASON:
            text = 'You cannot buy an upgrade while dead.'
        elif msg.reasonId == GAME_NOT_STARTED_REASON:
            text = 'Upgrades can' 't be bought at this time.'
        elif msg.reasonId == ALREADY_TURRET_REASON:
            text = 'There is already a turret in this zone.'
        elif msg.reasonId == TOO_CLOSE_TO_EDGE_REASON:
            text = 'You are too close to the zone edge.'
        elif msg.reasonId == TOO_CLOSE_TO_ORB_REASON:
            text = 'You are too close to the orb.'
        elif msg.reasonId == NOT_IN_DARK_ZONE_REASON:
            text = 'You are not in a dark friendly zone.'
        elif msg.reasonId == INVALID_UPGRADE_REASON:
            text = 'Upgrade not recognised by server.'
        elif msg.reasonId == DISABLED_UPGRADE_REASON:
            text = 'That upgrade is currently disabled.'
        else:
            text = 'You cannot buy that item at this time.'
        self.detailsInterface.newMessage(text)
        self.defaultHandler(msg)

    @PlayerHasUpgradeMsg.handler
    def gotUpgrade(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            self.detailsInterface.upgradeUsed(player, msg.upgradeType)
            upgradeClass = self.world.getUpgradeType(msg.upgradeType)
            existing = player.items.get(upgradeClass)
            if not existing:
                if (self.detailsInterface.player is None
                        or self.detailsInterface.player.isFriendsWith(player)):
                    if upgradeClass == Turret:
                        self.playSound('turret')
                    else:
                        self.playSound('buyUpgrade')

        self.defaultHandler(msg)

    @ChatFromServerMsg.handler
    def gotChatFromServer(self, msg):
        self.detailsInterface.newMessage(msg.text.decode(), error=msg.error)

    @TaggingZoneMsg.handler
    def zoneTagged(self, msg):
        try:
            zone = self.world.zoneWithId[msg.zoneId]
            zoneLabel = zone.defn.label
        except KeyError:
            zoneLabel = '<?>'

        if msg.playerId != NO_PLAYER:
            try:
                player = self.world.playerWithId[msg.playerId]
            except KeyError:
                nick = '<?>'
            else:
                nick = player.nick
            message = '%s tagged zone %s' % (nick, zoneLabel)

            self.detailsInterface.newMessage(message)

    def playerDied(self, target, killer, deathType):
        if deathType == OFF_MAP_DEATH:
            messages = [
                'fell into the void', 'looked into the abyss',
                'dug too greedily and too deep'
            ]
            message = '%s %s' % (target.nick, random.choice(messages))
        elif deathType == TROSBALL_DEATH:
            message = '%s was killed by the Trosball' % (target.nick, )
        elif deathType == BOMBER_DEATH:
            message = '%s head asplode' % (target.nick, )
            thisPlayer = self.detailsInterface.player
            if thisPlayer and target.id == thisPlayer.id:
                self.detailsInterface.doAction('no upgrade')
        else:
            if killer is None:
                message = '%s was killed' % (target.nick, )
                self.detailsInterface.newMessage(message)
            else:
                message = '%s killed %s' % (killer.nick, target.nick)

        self.detailsInterface.newMessage(message)

    @RespawnMsg.handler
    def playerRespawn(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s is back in the game' % (player.nick, )
            self.detailsInterface.newMessage(message)

    @CannotRespawnMsg.handler
    def respawnFailed(self, msg):
        if msg.reasonId == GAME_NOT_STARTED_REASON:
            message = 'The game has not started yet.'
        elif msg.reasonId == ALREADY_ALIVE_REASON:
            message = 'You are already alive.'
        elif msg.reasonId == BE_PATIENT_REASON:
            message = 'You cannot respawn yet.'
        elif msg.reasonId == ENEMY_ZONE_REASON:
            message = 'Cannot respawn outside friendly zone.'
        elif msg.reasonId == FROZEN_ZONE_REASON:
            message = 'That zone has been frozen!'
        else:
            message = 'You cannot respawn here.'
        self.detailsInterface.newMessage(
            message, self.app.theme.colours.errorMessageColour)

    def sendPrivateChat(self, player, targetId, text):
        self.sendRequest(ChatMsg(PRIVATE_CHAT, targetId, text=text.encode()))

    def sendTeamChat(self, player, text):
        self.sendRequest(ChatMsg(TEAM_CHAT, player.teamId, text=text.encode()))

    def sendPublicChat(self, player, text):
        self.sendRequest(ChatMsg(OPEN_CHAT, text=text.encode()))

    def openChat(self, text, sender):
        text = ': ' + text
        self.detailsInterface.newChat(text, sender)

    def teamChat(self, team, text, sender):
        player = self.detailsInterface.player
        if player and player.isFriendsWithTeam(team):
            text = " (team): " + text
            self.detailsInterface.newChat(text, sender)

    @AchievementUnlockedMsg.handler
    def achievementUnlocked(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if not player:
            return

        achievementName = self.achievementDefs.getAchievementDetails(
            msg.achievementId)[0]
        self.detailsInterface.newMessage(
            '%s has unlocked "%s"!' % (player.nick, achievementName),
            self.app.theme.colours.achievementMessageColour)

        focusPlayer = self.detailsInterface.player
        if (focusPlayer is not None and focusPlayer.id == msg.playerId):
            self.detailsInterface.localAchievement(msg.achievementId)

    @ShotFiredMsg.handler
    def shotFired(self, msg):
        self.defaultHandler(msg)
        try:
            shot = self.world.getShot(msg.shotId)
        except KeyError:
            return

        pos = shot.pos
        dist = self.distance(pos)
        self.playSound('shoot', self.getSoundVolume(dist))

    def grenadeExploded(self, pos, radius):
        self.gameViewer.worldgui.addExplosion(pos)
        dist = self.distance(pos)
        self.playSound('explodeGrenade', self.getSoundVolume(dist))

    def trosballExploded(self, player):
        self.gameViewer.worldgui.addTrosballExplosion(player.pos)
        dist = self.distance(player.pos)
        self.playSound('explodeGrenade', self.getSoundVolume(dist))

    @FireShoxwaveMsg.handler
    def shoxwaveExplosion(self, msg):
        localPlayer = self.localState.player
        if localPlayer and msg.playerId == localPlayer.id:
            return
        self.gameViewer.worldgui.addShoxwaveExplosion((msg.xpos, msg.ypos))

    def localShoxwaveFired(self):
        localPlayer = self.localState.player
        self.gameViewer.worldgui.addShoxwaveExplosion(localPlayer.pos)

    @UpgradeChangedMsg.handler
    def upgradeChanged(self, msg):
        self.detailsInterface.upgradeDisplay.refresh()

    def distance(self, pos):
        return distance(self.gameViewer.viewManager.getTargetPoint(), pos)

    def getSoundVolume(self, distance):
        'The volume for something that far away from the player'
        # Up to 500px away is within the "full sound zone" - full sound
        distFromScreen = max(0, distance - 500)
        # 1000px away from "full sound zone" is 0 volume:
        return 1 - min(1, (distFromScreen / 1000.))

    def playSound(self, action, volume=1):
        self.app.soundPlayer.play(action, volume)

    @PlaySoundMsg.handler
    def playSoundFromServerCommand(self, msg):
        self.app.soundPlayer.playFromServerCommand(
            msg.filename.decode('utf-8'))

    @TickMsg.handler
    def handle_TickMsg(self, msg):
        super(GameInterface, self).handle_TickMsg(msg)
        self.timingInfo.ticksSeen += 1
Exemplo n.º 11
0
class ListBox(Element):
    def __init__(self,
                 app,
                 area,
                 items,
                 font,
                 colour,
                 hlColour=None,
                 showBtns=True):
        super(ListBox, self).__init__(app)
        self.area = area
        self.items = items
        self.font = font
        self.showBtns = showBtns
        self.onValueChanged = Event()

        self.setColour(colour, hlColour)

        self.offset = 0
        self.index = -1

        # Create up and down buttons.
        if showBtns:
            img1 = TextImage('up', font, colour)
            img2 = TextImage('up', font, self.hlColour)
            self.upBtn = HoverButton(
                self.app,
                Location(AttachedPoint((0, 0), self._getRect, 'topright'),
                         'topright'), img1, img2)
            self.upBtn.onClick.addListener(self.upClick)
            img1 = TextImage('down', font, colour)
            img2 = TextImage('down', font, self.hlColour)
            self.dnBtn = HoverButton(
                self.app,
                Location(AttachedPoint((0, 0), self._getRect, 'bottomright'),
                         'bottomright'), img1, img2)
            self.dnBtn.onClick.addListener(self.dnClick)

    def _getRect(self):
        return self.area.getRect(self.app)

    def _getMaxInView(self):
        return int((self._getSize()[1] - 1) / self._getItemHeight()) + 1

    def _getItemHeight(self):
        return int(self.font.getLineSize(self.app))

    def setColour(self, colour, hlColour=None):
        self.colour = colour
        if hlColour:
            self.hlColour = hlColour
        else:
            self.hlColour = tuple((255 + i) / 2 for i in colour)

        self.bgColour = uniqueColour([self.hlColour, self.colour])

    def setItems(self, items):
        self.items = items
        if self.index >= len(self.items):
            self.index = -1

    def getItem(self, index=None):
        if index == None:
            index = self.index
        if index >= 0 and index < len(self.items):
            return self.items[index]
        raise ValueError, "Listbox doesn't have that many items"

    def getNumItems(self):
        return len(self.items)

    def setIndex(self, index):
        if index >= self.getNumItems():
            raise ValueError, "Listbox doesn't have that many items"
        self.index = index

    def getIndex(self):
        return self.index

    def _getSize(self):
        return self._getRect().size

    def _getPt(self):
        rect = self._getRect()
        return rect.topleft

    def draw(self, screen):
        rect = self._getRect()
        pos = rect.topleft

        self.offset = min(self.offset,
                          max(0,
                              len(self.items) - self._getMaxInView() + 1))

        # Draw the items.
        y = pos[1]
        for i in xrange(
                self.offset,
                min(self.offset + self._getMaxInView(), len(self.items))):
            text = self.font.render(self.app, self.items[i], True, self.colour)
            self._blitTextToScreen(screen, text, (pos[0], y))

            # Highlight selected one.
            if i == self.index:
                text = self.font.render(self.app, self.items[i], True,
                                        self.hlColour)
                hlOffset = int(self._getItemHeight() / 30. + .5)
                self._blitTextToScreen(screen, text,
                                       (pos[0] + hlOffset, y + hlOffset))

            y = y + self._getItemHeight()

        if self.showBtns:
            self.upBtn.draw(screen)
            self.dnBtn.draw(screen)

    def _blitTextToScreen(self, screen, textImage, placement):
        rect = self._getRect()
        textRect = textImage.get_rect()
        textRect.topleft = placement
        clippedRect = textRect.clip(rect)
        clippedRect.topleft = (0, 0)

        screen.blit(textImage, placement, clippedRect)

    def processEvent(self, event):
        if self.showBtns:
            event = self.upBtn.processEvent(event)
            if not event: return
            event = self.dnBtn.processEvent(event)
            if not event: return

        # Click on element selects it.
        if (event.type == pygame.MOUSEBUTTONDOWN
                and self._getRect().collidepoint(event.pos)):
            # Left-click
            if event.button == 1:
                rect = self._getRect()
                pos = rect.topleft
                index = ((event.pos[1] - pos[1]) / self._getItemHeight() +
                         self.offset)
                if index < len(self.items):
                    self.index = index
                    self.onValueChanged.execute(index)
                else:
                    return event
            # Scroll up
            elif event.button == 4:
                self._changeSelection(max(self.index - 1, 0))
            # Scroll down
            elif event.button == 5:
                self._changeSelection(min(self.index + 1, len(self.items) - 1))
            else:
                return event

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                self._changeSelection(max(self.index - 1, 0))
            elif event.key == pygame.K_DOWN:
                self._changeSelection(min(self.index + 1, len(self.items) - 1))
            elif event.key == pygame.K_PAGEUP:
                self._changeSelection(max(self.index - self._getMaxInView(),
                                          0))
            elif event.key == pygame.K_PAGEDOWN:
                self._changeSelection(
                    min(self.index + self._getMaxInView(),
                        len(self.items) - 1))
            elif event.key == pygame.K_HOME:
                self._changeSelection(0)
            elif event.key == pygame.K_END:
                self._changeSelection(len(self.items) - 1)
            else:
                return event
        else:
            return event
        return None

    def _changeSelection(self, newIndex):
        self.setIndex(newIndex)
        self._putSelectedInView()
        self.onValueChanged.execute(self.index)

    def _putSelectedInView(self):
        if self.offset + self._getMaxInView() <= self.index:
            self.offset = (min(self.index + 3, len(self.items)) -
                           self._getMaxInView())
        elif self.offset > self.index:
            self.offset = max(self.index - 2, 0)

    def tick(self, deltaT):
        if not self.showBtns: return

        self.upBtn.tick(deltaT)
        self.dnBtn.tick(deltaT)

    def upClick(self, btn):
        self.offset = max(self.offset - 1, 0)

    def dnClick(self, btn):
        self.offset = self.offset + 1
Exemplo n.º 12
0
class TrosnothServerFactory(MsgServer):
    messages = serverMsgs

    def __init__(
            self, game, noAuth=False, agentCallback=None, *args, **kwargs):
        self.game = game
        self.noAuth = noAuth
        self.agentCallback = agentCallback

        self.connectedClients = set()

        self.onShutdown = Event()       # ()
        self.onConnectionEstablished = Event(['protocol'])
        self.onConnectionLost = Event(['protocol'])

        self.running = True
        self._alreadyShutdown = False

    def checkGreeting(self, greeting):
        return (greeting == b'Trosnoth18')

    def startListening(self, port=DEFAULT_GAME_PORT, interface=''):
        try:
            self.port = reactor.listenTCP(port, self, interface=interface)
        except CannotListenError:
            log.warning('WARNING: Could not listen on port %s', port)
            self.port = reactor.listenTCP(0, self, interface=interface)

    def getTCPPort(self):
        return self.port.getHost().port

    def stopListening(self):
        self.port.stopListening()

    def gotBadString(self, protocol, data):
        log.warning('Server: Unrecognised network data: %r' % (data,))
        log.warning('      : Did you invent a new network message and forget')
        log.warning('      : to add it to '
                    'trosnoth.network.server.serverMsgs?')

    def connectionEstablished(self, protocol):
        '''
        Called by the network manager when a new incoming connection is
        completed.
        '''
        # Remember that this connection's ready for transmission.
        self.connectedClients.add(protocol)
        hub = LocalHub(
            self.game, noAuth=self.noAuth, agentCallback=self.agentCallback)
        hub.connectNode(protocol)

        # Send the setting information.
        protocol.gotServerCommand(InitClientMsg(self._getClientSettings()))

        self.onConnectionEstablished(protocol)

    def _getClientSettings(self):
        '''Returns a byte string representing the settings which must be
        sent to clients that connect to this server.'''

        result = self.game.world.dumpEverything()
        result['serverVersion'] = serverVersion

        return repr(result).encode('utf-8')

    def connectionLost(self, protocol, reason):
        if protocol in self.connectedClients:
            protocol.hub.disconnectNode()

        self.connectedClients.remove(protocol)
        self.onConnectionLost(protocol)

    def shutdown(self):
        if self._alreadyShutdown:
            return
        self._alreadyShutdown = True

        # Kill server
        self.running = False
        self.game.stop()
        self.onShutdown.execute()
Exemplo n.º 13
0
class CheckBox(Element):
    def __init__(self,
                 app,
                 pos,
                 text,
                 font,
                 colour,
                 initValue=False,
                 hotkey=None,
                 style='circle',
                 fillColour=None):
        super(CheckBox, self).__init__(app)

        self.pos = pos
        self.font = font

        if hasattr(pos, 'apply'):
            self.text = TextElement(
                app, ' ' + text, font,
                Location(
                    AttachedPoint(ScaledSize(self._getBoxSize()[0] / 5, 2),
                                  self._getBoxRect, 'midright'), 'midleft'),
                colour)
        else:
            self.text = TextElement(
                app, ' ' + text, font,
                Location((pos[0] + self._getBoxSize()[0],
                          pos[1] - self._getBoxSize()[0] / 10), 'topleft'),
                colour)

        self.value = initValue
        self.colour = colour
        if fillColour is None:
            self.fillColour = tuple((256 * 3 + i) / 4 for i in colour)
        else:
            self.fillColour = fillColour
        self.hotkey = hotkey
        self.style = style
        self.onValueChanged = Event()

    def _getRect(self):
        return self._getBoxRect().union(self.text._getRect())

    def _getBorderWidth(self):
        return max(1, self._getBoxSize()[0] / 8)

    def _getBoxSize(self):
        return (int(self.font.getHeight(self.app) / 1.5),
                int(self.font.getHeight(self.app) / 1.5))

    def _getBoxRect(self):
        if hasattr(self.pos, 'apply'):
            box = pygame.rect.Rect((0, 0), self._getBoxSize())
            self.pos.apply(self.app, box)
        else:
            box = pygame.rect.Rect(self.pos, self._getBoxSize())
        return box

    def draw(self, surface):
        box = self._getBoxRect()
        if self.value:
            if self.style == 'fill':
                pygame.draw.rect(surface, self.fillColour, box, 0)
            elif self.style == 'cross':
                pygame.draw.line(surface, self.fillColour, box.topright,
                                 box.bottomleft, self._getBorderWidth())
                pygame.draw.line(surface, self.fillColour, box.topleft,
                                 box.bottomright, self._getBorderWidth())
            elif self.style == 'circle':
                pygame.draw.circle(surface, self.fillColour, box.center,
                                   box.width / 2 - 2)
        pygame.draw.rect(surface, self.colour, box, self._getBorderWidth())

        self.text.draw(surface)

    def setValue(self, val):
        if val != self.value:
            self.value = val
            self.onValueChanged.execute(self)

    def getValue(self):
        return self.value

    def processEvent(self, event):
        box = self._getBoxRect()
        # Active events.
        if (event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and
                box.collidepoint(event.pos)) or (event.type == pygame.KEYDOWN
                                                 and event.key == self.hotkey):
            self.setValue(not self.value)
            self.onValueChanged.execute(self)
        else:
            return event
        return None
Exemplo n.º 14
0
class HoverButton(Element):
    '''A button which changes image when the mouse hovers over it.
    @param  pos     should be a trosnoth.ui.common.Location instance
    '''
    def __init__(self,
                 app,
                 pos,
                 stdImage,
                 hvrImage,
                 hotkey=None,
                 onClick=None):
        super(HoverButton, self).__init__(app)

        self.stdImage = stdImage
        self.hvrImage = hvrImage
        self.pos = pos
        self.onClick = Event()
        self.hotkey = hotkey

        self.mouseOver = False

        if onClick is not None:
            self.onClick.addListener(onClick)

    def _getSurfaceToBlit(self):
        img = self._getImageToUse()
        if hasattr(img, 'getImage'):
            return img.getImage(self.app)
        else:
            return img

    def _getImageToUse(self):
        if self.mouseOver:
            return self.hvrImage
        else:
            return self.stdImage

    def _getSize(self):
        img = self._getImageToUse()
        if hasattr(img, 'getSize'):
            return img.getSize(self.app)
        else:
            # pygame.surface.Surface
            return img.get_size()

    def _getPt(self):
        if hasattr(self.pos, 'apply'):
            rect = pygame.Rect((0, 0), self._getSize())
            self.pos.apply(self.app, rect)
            return rect.topleft
        else:
            return self.pos

    def _getRect(self):
        if hasattr(self.pos, 'apply'):
            rect = pygame.Rect((0, 0), self._getSize())
            self.pos.apply(self.app, rect)
            return rect
        return pygame.Rect(self.pos, self._getSize())

    def processEvent(self, event):
        rect = self._getRect()
        # Passive events.
        if event.type == pygame.MOUSEMOTION:
            self.mouseOver = rect.collidepoint(event.pos)

        # Active events.
        if (event.type == pygame.MOUSEBUTTONDOWN and event.button == 1
                and rect.collidepoint(event.pos)):
            self.onClick.execute(self)
        elif event.type == pygame.KEYDOWN and event.key == self.hotkey:
            self.onClick.execute(self)
        else:
            return event
        return None

    def draw(self, screen):
        # Draw the button.
        screen.blit(self._getSurfaceToBlit(), self._getPt())
Exemplo n.º 15
0
class ScrollingText(Element):
    def __init__(self, app, area, text, colour, fonts, bgColour=None,
            textAlign='left', loop=False, startOff=False, antiAlias=False):
        super(ScrollingText,self).__init__(app)

        self.area = area
        self.border = False
        self.shadowColour = None
        self.loop = loop
        self.startOff = startOff
        self.bgColour = bgColour
        self.speed = 50

        self.offset = 0
        self.colour = colour
        self.fonts = fonts
        self.text = text
        self.textAlign = textAlign
        self.antiAlias = antiAlias
        self.dirty = False

        # A test to see whether the size has changed
        self.lastSize = app.screenManager.size

        self._setImage()

        self.autoScroll = False


        # Create up and down buttons for if there's autoscroll.
        if 'buttons' in fonts:
            font = fonts['buttons']
        else:
            font = app.fonts.scrollingButtonsFont
        pos = Location(AttachedPoint((0, 0), self._getRect, 'topright'),
                'topleft')
        self.upBtn = TextButton(app, pos, ' up', font, colour, (255,255,255))
        pos = Location(AttachedPoint((0,0), self._getRect, 'bottomright'),
                'bottomleft')
        self.dnBtn = TextButton(app, pos, ' down', font, colour, (255,255,255))

        self.onFinishedScrolling = Event()

    def setColour(self, colour):
        self.colour = colour
        self.dirty = True
        pos = Location(AttachedPoint((0,0), self._getRect, 'topright'),
                'topleft')
        if 'buttons' in self.fonts:
            font = self.fonts['buttons']
        else:
            font = self.app.fonts.scrollingButtonsFont
        self.upBtn = TextButton(self.app, pos, ' up', font, colour,
                (255,255,255))
        self.dnBtn = TextButton(self.app, pos, ' down', font, colour,
                (255,255,255))

    def setAutoscroll(self, hasAutoscroll):
        self.autoScroll = hasAutoscroll

    def setSpeed(self, speed):
        self.speed = speed

    def setBorder(self, hasBorder):
        self.border = hasBorder

    def setShadowColour(self, shadowColour):
        self.shadowColour = shadowColour
        self.dirty = True

    def _setImage(self):
        mainImage = self._setImageDetails(self.text, self.colour, self.fonts,
                self.textAlign)
        if self.shadowColour is None:
            self.image = mainImage
        else:
            shadowImage = self._setImageDetails(self.text, self.shadowColour,
                    self.fonts, self.textAlign)
            shadowOffset = (1, 1)
            size = mainImage.get_size()
            self.image = pygame.Surface(tuple([size[i] + shadowOffset[i] for i
                    in (0,1)]))
            if self.bgColour:
                self.image.fill(self.bgColour)
            else:
                key = uniqueColour((self.colour, self.shadowColour))
                self.image.fill(key)
                self.image.set_colorkey(key)
            self.image.blit(shadowImage, shadowOffset)
            self.image.blit(mainImage, (0,0))
        self.dirty = False

    def _setImageDetails(self, text, colour, fonts, align):
        margin = 50

        lines = text.split('\n')
        rendered = []
        heading1Font = fonts['h1']
        heading2Font = fonts['h2']
        bodyFont = fonts['body']
        x = 0

        width = self._getSize()[0] - 2 * margin
        while x < len(lines):
            line = lines[x]
            if line.startswith("<h1>") and line.endswith("</h1>"):
                style = "h1"
                line = line[4:len(line)-5]
                font = heading1Font
            elif line.startswith("<h2>") and line.endswith("</h2>"):
                style = "h2"
                line = line[4:len(line)-5]
                font = heading2Font
            else:
                style = "body"
                font = bodyFont

            newLines = wrapWords(self.app, line, font, width)
            line = newLines[0]
            del newLines[0]

            # Insert the new lines into the list of lines
            newLines.reverse()
            for l in newLines:
                if style == "h1":
                    l = "<h1>" + l + "</h1>"
                elif style == "h2":
                    l = "<h2>" + l + "</h2>"
                lines.insert(x + 1, l)


            if self.bgColour:
                img = font.render(self.app, line, self.antiAlias, colour,
                        self.bgColour)
            else:
                img = font.render(self.app, line, self.antiAlias, colour)
            rendered.append(img)
            x += 1

        height = 0
        width = self._getSize()[0]
        for r in rendered:
            height += r.get_height()

        newImage = pygame.Surface((width, height))
        if self.bgColour:
            newImage.fill(self.bgColour)
        else:
            key = uniqueColour((colour),)
            newImage.fill(key)
            newImage.set_colorkey(key)
        yPos = 0
        for r in rendered:
            if align == 'left':
                xPos = margin
            elif align == 'right':
                xPos = width - r.get_width() - margin
            elif align == 'middle':
                xPos = (width - r.get_width()) / 2
            else:
                raise ValueError("Not a valid alignment argument")

            newImage.blit(r, (xPos, yPos))
            yPos += r.get_height()
        if self.startOff:
            if self.loop:
                size = (width, height + self._getSize()[1])
            else:
                size = (width, height + 2 * self._getSize()[1])
        else:
            size = (width, height)

        image = pygame.Surface(size)
        if not self.bgColour:
            image.fill(key)
            image.set_colorkey(key)
        if self.startOff:
            image.blit(newImage, (0, self._getSize()[1]))
        else:
            image.blit(newImage, (0, 0))

        self.canScroll = image.get_height() > self._getSize()[1]
        self.reachedEnd = (not self.loop and self.offset == 0) or (self.loop
                and not self.canScroll)

        return image

    def returnToTop(self):
        self.offset = 0

    def _getRect(self):
        return self.area.getRect(self.app)

    def _getSize(self):
        return self._getRect().size

    def _getPt(self):
        return self._getRect().topleft

    def draw(self, screen):
        if self.dirty or self.app.screenManager.size != self.lastSize:
            self.lastSize = self.app.screenManager.size
            self._setImage()
        if not self.canScroll:
            # Our image won't cover the whole of the scrollingText
            height = self._getSize()[1] - self.image.get_height()
            rect = pygame.Rect(self._getPt()[0], self._getPt()[1] +
                    self.image.get_height(), self._getSize()[0], height)
            if self.bgColour:
                screen.fill(self.bgColour, rect)
        # Find the segment which will be drawn on screen
        rect = pygame.Rect((0, self.offset), self._getSize())
        screen.blit(self.image, self._getPt(), rect)

        if (self.loop and self.canScroll and self.offset + self._getSize()[1] >
                self.image.get_height()):
            # It's doing the looping
            rect = pygame.Rect((0, self.offset - self.image.get_height()),
                    self._getSize())
            screen.blit(self.image, self._getPt(), rect)

        if self.border:
            rect.topleft = self._getPt()
            pygame.draw.rect(screen, (0,0,0), rect, 4)

        if not self.autoScroll:
            self.upBtn.draw(screen)
            self.dnBtn.draw(screen)

    def processEvent(self, event):
        if self.autoScroll:
            # We don't consume any events.
            return event
        else:
            event = self.upBtn.processEvent(event)
            if not event: return
            event = self.dnBtn.processEvent(event)
            if not event: return

            return event

    def scroll(self, offset):
        if not self.canScroll:
            return
        if self.reachedEnd:
            if ((offset > 0 and self.offset > 0) or
                    (offset < 0 and self.offset == 0)):
               # They have already reached the end, and are trying to
               # go further that way - do nothing.
               return
        self.reachedEnd = False
        newOffset = offset + self.offset
        if not self.loop:
            if newOffset + self._getSize()[1] > self.image.get_height():
                # They've hit the bottom
                newOffset = self.image.get_height() - self._getSize()[1]
                self.onFinishedScrolling.execute()
                self.reachedEnd = True
            elif newOffset <= 0:
                # Hit the top
                newOffset = 0
                self.onFinishedScrolling.execute()
                self.reachedEnd = True
        self.offset = newOffset

        # For looping: make sure it loops around
        if self.loop:
            self.offset %= self.image.get_height()


    def _getSpeed(self):
        if hasattr(self.speed, 'getSpeed'):
            speed = self.speed.getSpeed(self.app)
        else:
            speed = self.speed
        return speed

    def tick(self, deltaT):
        if self.autoScroll:
            self.scroll(self._getSpeed() * deltaT)
        else:
            if self.upBtn.mouseOver:
                self.scroll(-self._getSpeed() * deltaT)
            elif self.dnBtn.mouseOver:
                self.scroll(self._getSpeed() * deltaT)
            self.upBtn.tick(deltaT)
            self.dnBtn.tick(deltaT)
Exemplo n.º 16
0
class SavedGameTab(Tab):
    def __init__(self, app, tabContainer, onCancel=None, onReplay=None):
        super(SavedGameTab, self).__init__(app, 'Saved Games')
        self.app = app
        self.tabContainer = tabContainer
        self.onCancel = Event(listener=onCancel)
        self.onReplay = Event(listener=onReplay)

        font = self.app.screenManager.fonts.ampleMenuFont
        smallFont = self.app.screenManager.fonts.menuFont
        colours = app.theme.colours

        # Static text
        self.staticText = [
            TextElement(self.app, 'server details:', font,
                        ScaledLocation(960, 200, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'date and time:', font,
                        ScaledLocation(960, 370, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'replay:', font,
                        ScaledLocation(620, 550, 'topleft'),
                        colours.headingColour),
            TextElement(self.app, 'stats:', font,
                        ScaledLocation(620, 605, 'topleft'),
                        colours.headingColour)
        ]

        # Dynamic text
        self.listHeaderText = TextElement(self.app, 'available game files:',
                                          font, ScaledLocation(65, 200),
                                          colours.headingColour)
        self.noFiles1Text = TextElement(self.app, '', font,
                                        ScaledLocation(65, 260),
                                        colours.noGamesColour)
        self.noFiles2Text = TextElement(self.app, '', font,
                                        ScaledLocation(65, 310),
                                        colours.noGamesColour)
        self.serverNameText = TextElement(self.app, '', smallFont,
                                          ScaledLocation(960, 255, 'topright'),
                                          colours.startButton)
        self.serverDetailsText = TextElement(
            self.app, '', smallFont, ScaledLocation(960, 295, 'topright'),
            colours.startButton)
        self.dateText = TextElement(self.app, '', smallFont,
                                    ScaledLocation(960, 425, 'topright'),
                                    colours.startButton)
        self.lengthText = TextElement(self.app, '', smallFont,
                                      ScaledLocation(960, 465, 'topright'),
                                      colours.startButton)
        self.noReplayText = TextElement(self.app, '', smallFont,
                                        ScaledLocation(960, 550, 'topright'),
                                        colours.noGamesColour)
        self.noStatsText = TextElement(self.app, '', smallFont,
                                       ScaledLocation(960, 605, 'topright'),
                                       colours.noGamesColour)

        self.dynamicText = [
            self.listHeaderText, self.noFiles1Text, self.noFiles2Text,
            self.serverNameText, self.serverDetailsText, self.dateText,
            self.lengthText, self.noReplayText, self.noStatsText
        ]

        # Text buttons
        self.watchButton = TextButton(self.app,
                                      ScaledLocation(960, 550,
                                                     'topright'), '', font,
                                      colours.secondMenuColour, colours.white)
        self.watchButton.onClick.addListener(self.watchReplay)

        self.statsButton = TextButton(self.app,
                                      ScaledLocation(960, 605,
                                                     'topright'), '', font,
                                      colours.secondMenuColour, colours.white)
        self.statsButton.onClick.addListener(self.viewStats)

        self.refreshButton = TextButton(self.app,
                                        ScaledLocation(620, 665,
                                                       'topleft'), 'refresh',
                                        font, colours.secondMenuColour,
                                        colours.white)
        self.refreshButton.onClick.addListener(self.populateList)

        self.cancelButton = TextButton(self.app,
                                       ScaledLocation(960, 665, 'topright'),
                                       'cancel', font,
                                       colours.secondMenuColour, colours.white)
        self.cancelButton.onClick.addListener(self._cancel)

        self.loadFileButton = TextButton(
            self.app, ScaledLocation(960, 190, 'bottomright'), 'load file...',
            font, colours.mainMenuColour, colours.mainMenuHighlight)
        self.loadFileButton.onClick.addListener(self.showOpenDialog)

        self.buttons = [
            self.watchButton, self.statsButton, self.refreshButton,
            self.cancelButton, self.loadFileButton
        ]

        # Replay list
        self.gameList = ListBox(self.app, ScaledArea(65, 255, 500, 450), [],
                                smallFont, colours.listboxButtons)
        self.gameList.onValueChanged.addListener(self.updateSidebar)

        # Combine the elements
        self.elementsFiles = (self.staticText + self.dynamicText +
                              self.buttons + [self.gameList])
        self.elementsNoFiles = self.dynamicText + self.buttons

        # Populate the list of replays
        self.populateList()

    def _cancel(self, sender):
        self.onCancel.execute()

    def showOpenDialog(self, sender):
        root = Tk()
        root.withdraw()
        tksupport.install(root)
        filename = askopenfilename(
            defaultextension='.trosrepl',
            filetypes=[
                ('Trosnoth replay', '*.trosrepl'),
            ],
            initialdir=getPath(user, replayDir),
            title='Select replay',
        )
        if filename:
            self.onReplay.execute(filename)

    def populateList(self, sender=None):

        # Clear out the sidebar
        for item in self.dynamicText:
            item.setText('')
        self.listHeaderText.setText('available game files:')
        self.gameList.index = -1
        self.elements = self.elementsFiles[:]

        # Get a list of files with the name '*.tros'
        logDir = getPath(user, gameDir)
        makeDirs(logDir)
        fileList = []

        for fname in os.listdir(logDir):
            if os.path.splitext(fname)[1] == gameExt:
                fileList.append(fname)

        # Assume all files are valid for now
        validFiles = fileList[:]

        self.gameInfo = {}
        oldFound = False

        for fname in fileList:
            try:
                game = RecordedGame(os.path.join(logDir, fname))
            except RecordedGameException:
                validFiles.remove(fname)
                continue
            except:
                log.warning('invalid file: %s', fname)
                continue
            else:
                if game.recordedGameVersion != recordedGameVersion:
                    validFiles.remove(fname)
                    oldFound = True

            self.gameInfo[os.path.splitext(fname)[0]] = game

        # Sort the games with most recent first.
        items = [(v.unixTimestamp, n) for n, v in self.gameInfo.iteritems()]
        items.sort(reverse=True)
        items = [n for v, n in items]
        self.gameList.setItems(items)

        if len(self.gameInfo) == 0:
            self.elements = self.elementsNoFiles[:]
            self.listHeaderText.setText('0 available game files:')
            if oldFound:
                self.noFiles1Text.setText('Some games were found from')
                self.noFiles2Text.setText('previous Trosnoth versions')
            else:
                self.noFiles1Text.setText('You have not yet run any')
                self.noFiles2Text.setText('games on this computer')
        else:
            self.gameList.setIndex(0)
            self.updateSidebar(0)
            if len(self.gameInfo) == 1:
                self.listHeaderText.setText('1 available game file:')
            else:
                self.listHeaderText.setText('{} available game files:'.format(
                    len(self.gameInfo)))

    def updateSidebar(self, listID):
        # Update the details on the sidebar
        displayName = self.gameList.getItem(listID)

        # Server title
        self.serverNameText.setText(self.gameInfo[displayName].alias)

        # Date and time of match
        datePython = map(int, self.gameInfo[displayName].dateTime.split(','))
        dateString = time.strftime('%a %d/%m/%y, %H:%M', datePython)
        self.dateText.setText(dateString)

        # Length of match
        dateUnix = time.mktime(datePython)
        if self.gameInfo[displayName].wasFinished():
            lastUnix = self.gameInfo[displayName].gameFinishedTimestamp

            lengthSeconds = int(lastUnix - dateUnix)
            lengthMinutes, lengthSeconds = divmod(lengthSeconds, 60)

            secPlural = ('s', '')[lengthSeconds == 1]
            minPlural = ('s', '')[lengthMinutes == 1]
            if lengthMinutes == 0:
                lengthString = '{} second{}'.format(lengthSeconds, secPlural)
            else:
                lengthString = '{} min{}, {} sec{}'.format(
                    lengthMinutes, minPlural, lengthSeconds, secPlural)

            self.lengthText.setText(lengthString)
        else:
            self.lengthText.setText('')

        # Enable the replay button
        if (self.gameInfo[displayName].replayFilename is not None
                and os.path.exists(self.gameInfo[displayName].replayFilename)):
            self.watchButton.setText('watch')
            self.noReplayText.setText('')
        else:
            self.watchButton.setText('')
            self.noReplayText.setText('unavailable')

        # Enabled the stats button
        if (self.gameInfo[displayName].statsFilename is not None
                and os.path.exists(self.gameInfo[displayName].statsFilename)):
            self.statsButton.setText('view')
            self.noStatsText.setText('')
        else:
            self.statsButton.setText('')
            self.noStatsText.setText('unavailable')

    def watchReplay(self, sender):
        '''Watch replay button was clicked.'''
        # Try to create a replay server.
        self.onReplay.execute(
            self.gameInfo[self.gameList.getItem()].replayFilename)

    def viewStats(self, sender=None):
        '''View stats button was clicked.'''
        game = self.gameInfo[self.gameList.getItem()]

        self.htmlPath = game.generateHtmlFile()
        browser.openPage(self.app, self.htmlPath)

    def draw(self, surface):
        super(SavedGameTab, self).draw(surface)

        rect = self.tabContainer._getTabRect()
        verLineX = rect.left + (rect.width * 0.6)
        horLineY = rect.top + (rect.height * 0.68)

        colour = self.app.theme.colours.replayTabBorder

        pygame.draw.line(surface, colour, (verLineX, rect.top),
                         (verLineX, rect.bottom),
                         self.tabContainer._getBorderWidth())
        pygame.draw.line(surface, colour, (verLineX, horLineY),
                         (rect.right, horLineY),
                         self.tabContainer._getBorderWidth())
Exemplo n.º 17
0
class DisplaySettingsTab(Tab, framework.TabFriendlyCompoundElement):
    def __init__(self, app, onClose=None):
        super(DisplaySettingsTab, self).__init__(app, 'Display')

        self.onClose = Event()
        if onClose is not None:
            self.onClose.addListener(onClose)

        font = self.app.screenManager.fonts.bigMenuFont
        smallNoteFont = self.app.screenManager.fonts.smallNoteFont

        colour = self.app.theme.colours.headingColour

        def mkText(text, x, y, textFont=font, anchor='topright'):
            return TextElement(self.app, text, textFont,
                               ScaledLocation(x, y, anchor), colour)

        self.text = [
            mkText('X', 640, 280),
            mkText('Screen resolution', 430, 280),
            mkText('Fullscreen mode', 430, 360),
            mkText('Graphics detail', 430, 440),
            mkText('low', 460, 475, textFont=smallNoteFont, anchor='midtop'),
            mkText('high', 845, 475, textFont=smallNoteFont, anchor='midtop'),
            mkText('Show timings', 430, 525),
        ]

        self.invalidInputText = TextElement(self.app, '', font,
                                            ScaledLocation(512, 230, 'midtop'),
                                            (192, 0, 0))

        self.widthInput = prompt.InputBox(self.app,
                                          ScaledArea(460, 265, 150, 60),
                                          initValue=str(
                                              self.app.screenManager.size[0]),
                                          font=font,
                                          maxLength=4,
                                          validator=prompt.intValidator)

        self.widthInput.onEnter.addListener(lambda sender: self.saveSettings())
        self.widthInput.onClick.addListener(self.setFocus)
        self.widthInput.onTab.addListener(self.tabNext)

        self.heightInput = prompt.InputBox(self.app,
                                           ScaledArea(652, 265, 150, 60),
                                           initValue=str(
                                               self.app.screenManager.size[1]),
                                           font=font,
                                           maxLength=4,
                                           validator=prompt.intValidator)

        self.heightInput.onEnter.addListener(
            lambda sender: self.saveSettings())
        self.heightInput.onClick.addListener(self.setFocus)
        self.heightInput.onTab.addListener(self.tabNext)

        self.tabOrder = [self.widthInput, self.heightInput]

        self.fullscreenBox = CheckBox(
            self.app,
            ScaledLocation(460, 365),
            text='',
            font=font,
            colour=(192, 192, 192),
            initValue=self.app.screenManager.isFullScreen(),
        )
        self.fullscreenBox.onValueChanged.addListener(self.fullscreenChanged)

        displaySettings = app.displaySettings

        self.detailSlider = Slider(
            self.app,
            ScaledArea(460, 430, 390, 40),
            bounds=(0, len(displaySettings.DETAIL_LEVELS) - 1),
            snap=True)
        self.detailSlider.setVal(
            displaySettings.DETAIL_LEVELS.index(displaySettings.detailLevel))
        self.detailSlider.onValueChanged.addListener(self.detailChanged)

        self.showTimingsBox = CheckBox(
            self.app,
            ScaledLocation(460, 530),
            text='',
            font=font,
            colour=(192, 192, 192),
            initValue=displaySettings.showTimings,
        )

        self.input = [
            self.widthInput, self.heightInput, self.widthInput,
            self.fullscreenBox, self.detailSlider, self.showTimingsBox
        ]

        self.elements = self.text + self.input + [
            self.invalidInputText,
            button(app,
                   'save',
                   self.saveSettings, (-100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white),
            button(app,
                   'cancel',
                   self.cancelMenu, (100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white),
        ]
        self.setFocus(self.widthInput)

    def detailChanged(self, newLevel):
        pass

    def cancelMenu(self):
        self.fullscreenBox.setValue(self.app.screenManager.isFullScreen())
        self.showTimingsBox.setValue(self.app.displaySettings.showTimings)
        self.heightInput.setValue(str(self.app.screenManager.size[1]))
        self.widthInput.setValue(str(self.app.screenManager.size[0]))

        self.onClose.execute()

    def saveSettings(self):
        displaySettings = self.app.displaySettings

        height = self.getInt(self.heightInput.value)
        width = self.getInt(self.widthInput.value)
        fullScreen = self.fullscreenBox.value
        detailLevel = displaySettings.DETAIL_LEVELS[self.detailSlider.getVal()]
        showTimings = self.showTimingsBox.value

        # The resolutionList is used when fullScreen is true.
        resolutionList = pygame.display.list_modes()
        resolutionList.sort()
        minResolution = resolutionList[0]
        maxResolution = resolutionList[-1]

        if not fullScreen:
            minResolution = (320, 240)

        # These values are used when fullScreen is false.
        widthRange = (minResolution[0], maxResolution[0])
        heightRange = (minResolution[1], maxResolution[1])

        if not widthRange[0] <= width <= widthRange[1]:
            self.incorrectInput('Screen width must be between %d and %d' %
                                (widthRange[0], widthRange[1]))
            width = None
            return
        if not heightRange[0] <= height <= heightRange[1]:
            self.incorrectInput('Screen height must be between %d and %d' %
                                (heightRange[0], heightRange[1]))
            height = None
            return
        if fullScreen:
            selectedResolution = (width, height)
            if selectedResolution not in resolutionList:
                self.incorrectInput('Selected resolution is not valid for '
                                    'this display')
                height = width = None
                return

        self.incorrectInput('')

        # Save these values.
        displaySettings.fullScreen = fullScreen
        displaySettings.detailLevel = detailLevel
        displaySettings.showTimings = showTimings
        if fullScreen:
            displaySettings.fsSize = (width, height)
        else:
            displaySettings.size = (width, height)

        # Write to file and apply.
        displaySettings.save()
        displaySettings.apply()

        self.onClose.execute()

    def getInt(self, value):
        if value == '':
            return 0
        return int(value)

    def incorrectInput(self, string):
        self.invalidInputText.setText(string)
        self.invalidInputText.setFont(self.app.screenManager.fonts.bigMenuFont)

    def fullscreenChanged(self, element):
        # If the resolution boxes haven't been touched, swap their values to
        # the appropriate resolution for the new mode.

        height = self.getInt(self.heightInput.value)
        width = self.getInt(self.widthInput.value)
        fullScreen = self.fullscreenBox.value

        if fullScreen:
            # Going to full screen mode.
            if (width, height) != self.app.displaySettings.size:
                return
            width, height = self.app.displaySettings.fsSize
        else:
            # Going from full screen mode.
            if (width, height) != self.app.displaySettings.fsSize:
                return
            width, height = self.app.displaySettings.size

        self.heightInput.setValue(str(height))
        self.widthInput.setValue(str(width))
Exemplo n.º 18
0
class KeymapTab(Tab):
    def __init__(self, app, onClose=None):
        super(KeymapTab, self).__init__(app, 'Controls')
        self.font = app.screenManager.fonts.bigMenuFont

        self.onClose = Event()
        if onClose is not None:
            self.onClose.addListener(onClose)

        # Break things up into categories
        movement = ['jump', 'down', 'left', 'right']
        menus = ['menu', 'more actions']
        actions = [
            'respawn', 'select upgrade', 'activate upgrade', 'change nickname',
            'ready'
        ]
        misc = ['chat', 'follow']
        upgrades = [
            upgradeClass.action for upgradeClass in sorted(
                allUpgrades, key=lambda upgradeClass: upgradeClass.order)
        ]
        upgrades.append('no upgrade')

        display = ['leaderboard', 'toggle interface', 'toggle terminal']

        actionNames = {
            'select upgrade': 'Select upgrade',
            'activate upgrade': 'Activate upgrade',
            'change nickname': 'Change nickname',
            'chat': 'Chat',
            'down': 'Drop down',
            'follow': 'Auto pan (replay)',
            'jump': 'Jump',
            'leaderboard': 'Show leaderboard',
            'left': 'Move left',
            'menu': 'Main menu',
            'more actions': 'Advanced',
            'no upgrade': 'Deselect upgrade',
            'ready': 'Toggle ready',
            'respawn': 'Respawn',
            'right': 'Move right',
            'status bar': 'Status bar',
            'timer': 'Show timer',
            'toggle interface': 'Toggle HUD',
            'toggle terminal': 'Toggle terminal',
            'zone progress': 'Show zone bar',
        }
        actionNames.update((upgradeClass.action, upgradeClass.name)
                           for upgradeClass in allUpgrades)

        # Organise the categories by column
        self.layout = [
            [movement, menus],
            [actions, display],
            [upgrades, misc],
        ]

        self.errorInfo = TextElement(self.app, '', self.font,
                                     ScaledLocation(512, 580, 'center'))
        self.text = [self.errorInfo]
        self.inputLookup = {}
        xPos = 190

        # Lay everything out automatically.
        keymapFont = self.app.screenManager.fonts.keymapFont
        keymapInputFont = self.app.screenManager.fonts.keymapInputFont
        for column in self.layout:  # Each column
            yPos = 200
            for category in column:  # Each category
                for action in category:  # Each action
                    # Draw action name (eg. Respawn)
                    self.text.append(
                        TextElement(self.app, actionNames[action], keymapFont,
                                    ScaledLocation(xPos, yPos + 6, 'topright'),
                                    self.app.theme.colours.headingColour))

                    # Create input box
                    box = prompt.KeycodeBox(self.app,
                                            ScaledArea(xPos + 10, yPos, 100,
                                                       30),
                                            font=keymapInputFont)
                    box.onClick.addListener(self.setFocus)
                    box.onChange.addListener(self.inputChanged)
                    box.__action = action
                    self.inputLookup[action] = box

                    yPos += 35  # Between items
                yPos += 35  # Between categories
            xPos += 320  # Between columns

        self.elements = self.text + self.inputLookup.values() + [
            button(app,
                   'restore default controls',
                   self.restoreDefaults, (0, -125),
                   'midbottom',
                   secondColour=app.theme.colours.white),
            button(app,
                   'save',
                   self.saveSettings, (-100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white),
            button(app,
                   'cancel',
                   self.cancel, (100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white),
        ]

        self.populateInputs()

    def inputChanged(self, box):
        # Remove the old key.
        try:
            oldKey = self.keyMapping.getkey(box.__action)
        except KeyError:
            pass
        else:
            del self.keyMapping[oldKey]

        # Set the new key.
        self.keyMapping[box.value] = box.__action

        # Refresh the display.
        self.refreshInputs()

    def populateInputs(self):
        # Set up the keyboard mapping.
        self.keyMapping = keyboard.KeyboardMapping(keymap.default_game_keys)

        try:
            # Try to load keyboard mappings from the user's personal settings.
            config = open(getPath(user, 'keymap'), 'rU').read()
            self.keyMapping.load(config)
        except IOError:
            pass

        # Refresh the display.
        self.refreshInputs()

    def refreshInputs(self):
        for column in self.layout:
            for category in column:
                for action in category:
                    # Get the current key and put it in the box.
                    try:
                        key = self.keyMapping.getkey(action)
                    except KeyError:
                        key = None
                    self.inputLookup[action].value = key

                    # Make the box white
                    self.inputLookup[action].backColour = (255, 255, 255)

    def restoreDefaults(self):
        self.keyMapping = keyboard.KeyboardMapping(keymap.default_game_keys)
        self.refreshInputs()

        self.incorrectInput(
            "Default controls restored: press 'save' to "
            "confirm", (0, 128, 0))

    def clearBackgrounds(self):
        for action in self.inputLookup:
            self.inputLookup[action].backColour = (255, 255, 255)
        self.setFocus(None)

    def saveSettings(self):
        # Perform the save.
        open(getPath(user, 'keymap'), 'w').write(self.keyMapping.save())

        emptyBoxes = []

        for box in self.inputLookup.itervalues():
            if box.value is None:
                emptyBoxes.append(box)

        if len(emptyBoxes) > 0:
            self.populateInputs()
            for box in emptyBoxes:
                box.backColour = self.app.theme.colours.invalidDataColour

            self.incorrectInput('Warning: some actions have no key',
                                (192, 0, 0))
        else:
            self.mainMenu()

    def incorrectInput(self, string, colour):
        self.errorInfo.setColour(colour)
        self.errorInfo.setText(string)
        self.errorInfo.setFont(self.font)

    def cancel(self):
        self.populateInputs()
        self.mainMenu()

    def mainMenu(self):
        self.incorrectInput('', (0, 0, 0))
        self.clearBackgrounds()
        self.onClose.execute()
Exemplo n.º 19
0
class TabContainer(CompoundElement):
    def __init__(self, app, area, font, borderColour=(0, 0, 0)):
        super(TabContainer, self).__init__(app)

        self.onTabSelected = Event()

        # area is that of the entire space including tab headers
        self.area = area
        self.font = font

        self._borderWidth = ScaledScalar(3)
        self.borderColour = borderColour

        nextTab = lambda: self._tabSelected(
            (self.selectedIndex + 1) % len(self.tabs))
        lastTab = lambda: self._tabSelected(
            (self.selectedIndex - 1) % len(self.tabs))
        self.nextTabKey = Hotkey(app, pygame.K_TAB, pygame.KMOD_CTRL)
        self.nextTabKey.onTriggered.addListener(nextTab)
        self.lastTabKey = Hotkey(app, pygame.K_TAB,
                                 pygame.KMOD_CTRL | pygame.KMOD_SHIFT)
        self.lastTabKey.onTriggered.addListener(lastTab)

        self.tabs = []
        self.tabHeaders = TabHeaderRow(app, self)
        self.selectedIndex = -1

        self.tabHeaders._update()

        self.elements = [self.tabHeaders, self.lastTabKey, self.nextTabKey]
        self.tabHeaders._update()

    def _getBorderWidth(self):
        if hasattr(self._borderWidth, 'getVal'):
            return int(self._borderWidth.getVal(self.app))
        else:
            return self._borderWidth

    def _getHeaderHeight(self):
        linesize = self.font.getLineSize(self.app)
        return int(linesize * 1.1)

    ##
    # Full rect (including headers)
    def _getRect(self):
        return self.area.getRect(self.app)

    def _getSize(self):
        return self._getRect().size

    def _getPt(self):
        return self._getRect().topleft

    ##
    # For the internal rect (not including headers)
    def _getTabPos(self):
        pos = self._getPt()
        return (pos[0], pos[1] + self._getHeaderHeight())

    def _getTabSize(self):
        size = self._getSize()
        return (size[0], size[1] - self._getHeaderHeight())

    def _getTabRect(self):
        return pygame.Rect(self._getTabPos(), self._getTabSize())

    def _getTabInternalRect(self):
        '''
        Returns the pygame.Rect of area which the tab should draw on. (When the
        tab draws it is relative to the top-left of the tab container.)
        '''
        return pygame.Rect((0, self._getHeaderHeight()), self._getTabSize())

    def selectTab(self, index):
        self._tabSelected(index)

    def _tabSelected(self, tab):
        if self.selectedIndex != -1:
            if not self.tabs[self.selectedIndex].beforeDeactivating():
                return
            self.tabs[self.selectedIndex].deactivated()
        if isinstance(tab, Tab):
            assert tab in self.tabs, "This tab must be in our list"
            self.selectedIndex = self.tabs.index(tab)
        else:
            # By index
            self.selectedIndex = tab

        # Notify those who care:
        self.tabHeaders.tabSelected(self.selectedIndex)
        self.onTabSelected.execute(self.selectedIndex)
        self.tabs[self.selectedIndex].activated()

    def addTab(self, tab):
        self.tabs.append(tab)
        tab.container = self

        newHeader = TabHeader(self.app, tab, self, self.font)
        self.tabHeaders.newTabHeader(newHeader)

        self.tabHeaders._update()
        # If it's the only tab, select it
        if self.selectedIndex == -1:
            self._tabSelected(tab)

    def renameTab(self, newName, tab):
        tab.caption = newName
        self.tabHeaders._update()

    def removeTabAt(self, index):
        tab = self.tabs.pop(index)
        tab.container = None

        self.tabHeaders.delTabHeader(index)
        assert len(self.tabs) == len(self.tabHeaders)
        assert len(self.tabs) != 0
        if index == self.selectedIndex:
            self.selectedIndex = -1
            self._tabSelected(min(index, len(self.tabs) - 1))
        self.tabHeaders._update()

    def removeTab(self, tab):
        tab.container = None
        index = self.tabs.index(tab)
        self.removeTabAt(index)

    def draw(self, screen):
        super(TabContainer, self).draw(screen)
        assert len(self.tabs) > 0, ('Need to have at least one tab before '
                                    'drawing a tabContainer')
        self.tabs[self.selectedIndex].draw(screen)
        # Draw a border
        pygame.draw.rect(screen, self.borderColour, self._getTabRect(),
                         self._getBorderWidth())

    def tick(self, deltaT):
        super(TabContainer, self).tick(deltaT)
        for i in range(0, len(self.tabs)):
            # tick all the tabs
            self.tabs[i].tick(deltaT)

    def processEvent(self, event):
        event = super(TabContainer, self).processEvent(event)
        if event:
            event = self.tabs[self.selectedIndex].processEvent(event)
        return event
Exemplo n.º 20
0
class MoveableBox(CompoundElement):
    defaultBorderColour = (0, 0, 255)
    defaultTitleColour = (255, 255, 255)
    defaultBackgroundColour = (255, 255, 255)

    def __init__(self, app, size, caption, subCaption=None):
        CompoundElement.__init__(self, app)
        self.showing = False
        self.size = size
        self._edge = MoveableBoxEdge(self.app, self, caption, subCaption)

        self.setColours(self.defaultBorderColour, self.defaultTitleColour,
                        self.defaultBackgroundColour)

        self.onClose = Event()

    def _giveFocus(self):
        self.app.screenManager.dialogFocus(self)

    def _getSize(self):
        if hasattr(self.size, 'getSize'):
            return self.size.getSize(self.app)
        else:
            return self.size

    def _setPos(self, pos):
        self._edge._setPos(pos)

    def setColours(self,
                   borderColour=None,
                   titleColour=None,
                   backgroundColour=None):
        if borderColour:
            self._edge.borderColour = borderColour
        if titleColour:
            self._edge.titleColour = titleColour
        if backgroundColour:
            self.backgroundColour = backgroundColour

    def setCaption(self, caption=None, subCaption=None):
        if caption is not None:
            self._edge.caption = caption
        if subCaption is not None:
            if subCaption == False:
                self._edge.subCaption = None
            else:
                self._edge.subCaption = subCaption

    def _getPt(self):
        return self._edge._getInsideArea()

    def _getInsideRect(self):
        return pygame.Rect(self._edge._getInsideTopLeftPt(), self._getSize())

    def draw(self, screen):
        self._edge.draw(screen)
        subSurface = pygame.Surface(self._getSize())
        subSurface.fill(self.backgroundColour)
        CompoundElement.draw(self, subSurface)
        screen.blit(subSurface, self._edge._getInsideTopLeftPt())

    def processEvent(self, event):
        event = self._edge.processEvent(event)
        if event:
            if hasattr(event, 'pos'):
                event2 = translateEvent(event,
                                        self._edge._getInsideTopLeftPt())
                isPos = True
            else:
                event2 = event
                isPos = False
            event2 = CompoundElement.processEvent(self, event2)
            if event2 == None:
                return None
            elif isPos and self._edge._getEdgeRect().collidepoint(event.pos):
                return None
            else:
                return event

    def show(self):
        try:
            showDialog = self.app.screenManager.showDialog
        except AttributeError:
            raise MultiWindowException(
                'Dialog Boxes cannot be used unless the Application is a '
                'MultiWindowApplication')
        showDialog(self)
        self.showing = True

    def close(self):
        try:
            closeDialog = self.app.screenManager.closeDialog
        except AttributeError:
            raise MultiWindowException(
                'Dialog Boxes cannot be used unless the '
                'Application is a MultiWindowApplication')
        closeDialog(self)
        self.showing = False
        self.onClose.execute()
Exemplo n.º 21
0
class PlayAuthScreen(framework.CompoundElement):
    passwordGUIFactory = PasswordGUI

    def __init__(self, app, onSucceed=None, onFail=None):
        super(PlayAuthScreen, self).__init__(app)
        self.onSucceed = Event(listener=onSucceed)
        self.onFail = Event(listener=onFail)
        self.lobby = None
        self.badServers = set()

        if app.displaySettings.alphaOverlays:
            alpha = 192
        else:
            alpha = None
        bg = SolidRect(app, app.theme.colours.playMenu, alpha,
                       Region(centre=Canvas(512, 384), size=Canvas(924, 500)))

        colour = app.theme.colours.mainMenuColour
        font = app.screenManager.fonts.consoleFont
        self.logBox = LogBox(
            app, Region(size=Canvas(900, 425), midtop=Canvas(512, 146)),
            colour, font)

        font = app.screenManager.fonts.bigMenuFont
        cancel = TextButton(app,
                            Location(Canvas(512, 624), 'midbottom'),
                            'Cancel',
                            font,
                            app.theme.colours.secondMenuColour,
                            app.theme.colours.white,
                            onClick=self.cancel)
        self.cancelled = False
        self.elements = [bg, self.logBox, cancel]
        self._passwordGetter = None
        self._gameSelectionBox = None

    @property
    def passwordGetter(self):
        if self._passwordGetter is None:
            self._passwordGetter = self.passwordGUIFactory(self.app)
        return self._passwordGetter

    @defer.inlineCallbacks
    def begin(self, servers, canHost=True):
        self.cancelled = False
        self.passwordGetter.setCancelled(False)
        self.badServers = set()
        if self.lobby is None:
            self.lobby = Lobby(self.app)

        # Removes the third item (http) from the tuple since we don't care about
        # it.
        servers = [(server[:2] if isinstance(server, tuple) else server)
                   for server in servers]

        for server in servers:
            if self.cancelled:
                break

            if server == JOIN_LOCAL_GAME:
                if self.app.server is not None:
                    self.onSucceed.execute()
                    self.app.interface.connectToLocalServer()
                    return

            elif isinstance(server, tuple):
                if server in self.badServers:
                    continue

                self.logBox.log('Requesting games from %s:%d...' % server)
                connected = yield self.attemptServerConnect(
                    self.lobby.getGames, server)
                if connected:
                    return

            elif server == JOIN_LAN_GAME:
                self.logBox.log('Asking local network for other games...')
                games = yield self.lobby.get_adhoc_lan_games()
                for game in games:
                    joinSuccessful = yield self.attemptJoinGame(game)
                    if joinSuccessful:
                        return

            elif server == JOIN_AUTH_LAN_GAME:
                self.logBox.log('Searching for servers on local network...')
                server = yield get_multicast_server()
                if server:
                    self.logBox.log(
                        'Server found. Requesting games from %s:%d...' %
                        server)
                    connected = yield self.attemptServerConnect(
                        self.lobby.getGames, server)
                    if connected:
                        return

        if canHost:
            if not self.cancelled:
                result = yield HostGameQuery(self.app).run()

                if not result:
                    self.onFail.execute()
                    return

                self.app.startListenServer(2, 1)

                self.onSucceed.execute()
                self.app.interface.connectToLocalServer()
        else:
            if not self.cancelled:
                box = OkBox(self.app, ScaledSize(450, 150), 'Trosnoth',
                            'Connection unsuccessful.')
                box.onClose.addListener(self.onFail.execute)
                box.show()

    @defer.inlineCallbacks
    def attemptServerConnect(self, getGamesList, server):
        '''
        Attempts to connect to a game on the server, returned by
        getGamesList(server).
        '''
        try:
            games = yield getGamesList(server)
        except ConnectError:
            self.logBox.log('Unable to connect.')
            self.badServers.add(server)
        except amp.UnknownRemoteError:
            self.logBox.log('Error on remote server.')
        except amp.RemoteAmpError as e:
            self.logBox.log('Error on remote server.')
            log.error('Remote error getting games list: %s', e)
        except IOError as e:
            self.logBox.log('Error connecting to remote server.')
            log.error('Error connecting to auth server: %s', e)
            self.badServers.add(server)
        except Exception:
            self.logBox.log('Error retrieving games list.')
            log.exception('Error retrieving games list')
        else:
            if len(games) == 0:
                self.logBox.log('No running games.')
            elif len(games) == 1:
                joinSuccessful = yield self.attemptJoinGame(games[0])
                defer.returnValue(joinSuccessful)
            else:
                game = yield self.selectGame(server, games)
                if game:
                    joinSuccessful = yield self.attemptJoinGame(game)
                    defer.returnValue(joinSuccessful)
                self.logBox.log('No game selected.')

        defer.returnValue(False)

    def selectGame(self, server, games):
        host, port = server
        if self._gameSelectionBox is None:
            self._gameSelectionBox = GameSelectionBox(self.app)
        return self._gameSelectionBox.selectGame(host, games)

    @defer.inlineCallbacks
    def attemptJoinGame(self, game):
        '''
        Attempts to join the given game.
        '''
        try:
            self.logBox.log('Found game: joining.')
            result = yield game.join(self.passwordGetter)
        except authcommands.GameDoesNotExist:
            pass
        except AuthenticationCancelled:
            pass
        except authcommands.NotAuthenticated:
            self.logBox.log('Authentication failure.')
        except amp.UnknownRemoteError:
            self.logBox.log('Error on remote server.')
        except amp.RemoteAmpError as e:
            self.logBox.log('Error on remote server.')
            log.error('Remote error joining game: %s', e)
        except ConnectionFailed:
            self.logBox.log('Could not connect.')
        except ConnectionRefusedError:
            self.logBox.log('Connection refused.')
        except IOError as e:
            self.logBox.log('Error connecting to remote server.')
            log.error('Error attempting to join game: %s', e)
        except Exception:
            self.logBox.log('Error connecting to remote server.')
            log.exception('Error joining game.')
        else:
            self._joined(result)
            defer.returnValue(True)
            return

        defer.returnValue(False)

    def _joined(self, result):
        self.onSucceed.execute()
        self.app.interface.connectedToGame(*result)

    def cancel(self, element):
        self.cancelled = True
        self.passwordGetter.setCancelled(True)
        self.onFail.execute()

    def processEvent(self, event):
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            self.onFail.execute()
        else:
            return super(PlayAuthScreen, self).processEvent(event)
Exemplo n.º 22
0
class ServerSideBotProcessManager:
    def __init__(self, game, logPrefix):
        self.game = game
        self.logPrefix = logPrefix
        self.process = None
        self.starting = None
        self.token = None
        self.waitForAMPConnection = None
        self.waitForProcessStart = None
        self.amp = None
        self.server = None
        self.joiningAgents = []

    def stop(self):
        if self.server:
            self.server.stopListening()
            self.server = None

    async def startBot(self, aiName, fromLevel=False, nick='', team=None):
        if self.server is None:
            self.server = TrosnothServerFactory(
                self.game, noAuth=True, agentCallback=self.agentConnected)
            self.server.startListening(port=0, interface=LOCALHOST)

        await self.ensureProcessHasStarted()

        authTag = len(self.joiningAgents)
        agentHasJoined = defer.Deferred()
        self.joiningAgents.append((agentHasJoined, fromLevel))

        await self.amp.callRemote(
            StartBot, name=aiName, fromLevel=fromLevel, nick=nick,
            teamId=team.id if team else NEUTRAL_TEAM_ID,
            authTag=authTag,
        )
        agent = await agentHasJoined

        return agent

    def agentConnected(self, agent, authTag):
        d, fromLevel = self.joiningAgents[authTag]
        self.joiningAgents[authTag] = None
        while self.joiningAgents and self.joiningAgents[-1] is None:
            self.joiningAgents.pop()
        agent.allowBotPlayer(fromLevel)
        d.callback(agent)

    async def ensureProcessHasStarted(self):
        if self.starting:
            await self.starting.waitOrRaise()
            return
        if self.process:
            return

        success = False
        self.starting = Event(['result'])
        try:
            await self._startProcess()
            success = True
        except Exception as e:
            self.starting.execute(e)
            raise
        else:
            self.starting.execute(None)
        finally:
            self.starting = None
            if not success:
                self.process = None
                self.amp = None

    async def _startProcess(self):
        assert self.process is None
        self.process = BotProcessProtocol(self)
        self.token = uuid.uuid4().hex.encode('ascii')
        self.waitForAMPConnection = defer.Deferred()

        ampFactory = Factory.forProtocol(ArenaSideAMPProtocol)
        ampFactory.manager = self
        ampEndpoint = TCP4ServerEndpoint(reactor, 0)
        listeningPort = await ampEndpoint.listen(ampFactory)
        try:
            ampPort = listeningPort.getHost().port
            cmd = self.getCommand() + [str(ampPort)]
            if self.logPrefix:
                cmd += [self.logPrefix]
            log.info('Running command %r', cmd)
            self.waitForProcessStart = defer.Deferred()
            transport = reactor.spawnProcess(
                self.process, cmd[0], cmd,
                env=None,
                childFDs=None if os.name == 'nt' else {0: 'w', 1: 1, 2: 2})

            try:
                await self.waitForProcessStart
            finally:
                self.waitForProcessStart = None
            self.process.transport.write(self.token + b'\n')

            try:
                self.amp = await self.waitForAMPConnection
            finally:
                self.waitForAMPConnection = None

            await self.amp.callRemote(
                ConnectToServer, port=self.server.port.getHost().port)
        finally:
            listeningPort.stopListening()

    def getCommand(self):
        if getattr(sys, 'frozen', False):
            # Bundled by PyInstaller
            path = os.path.dirname(sys.executable)
            ext = '.exe' if os.name == 'nt' else ''
            return [os.path.join(path, 'support' + ext), 'bots']

        return [sys.executable, __file__]

    def ampConnectionLost(self):
        if self.process:
            log.warning('Lost AMP connection to bot subprocess')
            self.process.killProcess()
Exemplo n.º 23
0
class PlayConnector(object):
    '''
    Connects to the first available game based on the configured server
    settings.

    passwordGetFunction must be a function that satisfies the
    PasswordGetter.getPassword interface from trosnoth.network.lobby.

    hostGameQueryFunction must be a function that returns a deferred which
    prompts the user to decide whether to host a game and returns the user's
    decision.
    '''
    def __init__(self,
                 app,
                 onSucceed=None,
                 onFail=None,
                 onLogLine=None,
                 passwordGetFunction=None,
                 hostGameQueryFunction=None):
        self.app = app
        self.onSucceed = Event(listener=onSucceed)
        self.onFail = Event(listener=onFail)
        self.onLogLine = Event(listener=onLogLine)
        self.passwordGetFunction = passwordGetFunction
        self.hostGameQueryFunction = hostGameQueryFunction
        self.lobby = None
        self.badServers = set()
        self.cancelled = False
        self.running = False

    def getPassword(self, host, errorText=''):
        if self.passwordGetFunction is not None:
            return self.passwordGetFunction(host, errorText)
        return None

    @defer.inlineCallbacks
    def begin(self, servers, canHost=True):
        '''
        Attempts to connect to the given servers in turn, and if canHost is
        given will prompt the user to host a game if no other games are
        available.

        Calling this function will always trigger either onSucceed or onFail to
        be executed unless the user selects cancel.
        '''
        if self.running:
            raise RuntimeError('Already running')
        self.running = True

        self.cancelled = False
        self.badServers = set()
        if self.lobby is None:
            self.lobby = Lobby(self.app)

        for server in servers:
            if self.cancelled:
                break

            if server == JOIN_LOCAL_GAME:
                localGame = self.app.hoster.getGameObject()
                if localGame is not None:
                    self.onSucceed.execute()
                    self.app.connector.openGameObject(localGame)
                    self.running = False
                    return

            elif isinstance(server, tuple):
                host, port = server[:2]
                if (host, port) in self.badServers:
                    continue

                self.onLogLine('Requesting games from {}:{}...'.format(
                    host, port))
                connected = yield self.attemptServerConnect(
                    self.lobby.getGames, (host, port))
                if connected:
                    self.running = False
                    return

            elif server == JOIN_LAN_GAME:
                self.onLogLine('Asking local network for other games...')
                games = yield self.lobby.get_adhoc_lan_games()
                for game in games:
                    joinSuccessful = yield self.attemptJoinGame(game)
                    if joinSuccessful:
                        self.running = False
                        return

        if canHost:
            if not self.cancelled:
                result = yield self.hostGameQueryFunction()

                if not result:
                    self.onFail.execute()
                    self.running = False
                    return

                self.app.hoster.startServer(halfMapWidth=2, mapHeight=1)
                self.onSucceed.execute()
                localGame = self.app.hoster.getGameObject()
                self.app.connector.openGameObject(localGame)
        else:
            if not self.cancelled:
                self.onFail.execute()

        self.running = False

    @defer.inlineCallbacks
    def attemptServerConnect(self, getGamesList, server):
        '''
        Attempts to connect to a game on the server, returned by
        getGamesList(server).
        '''
        try:
            games = yield getGamesList(server)
        except ConnectError:
            self.onLogLine('Unable to connect.')
            self.badServers.add(server)
        except amp.UnknownRemoteError:
            self.onLogLine('Error on remote server.')
        except amp.RemoteAmpError as e:
            self.onLogLine('Error on remote server.')
            log.error('Remote error getting games list: %s', e)
        except IOError as e:
            self.onLogLine('Error connecting to remote server.')
            log.error('Error connecting to auth server: %s', e)
            self.badServers.add(server)
        except Exception:
            self.onLogLine('Error retrieving games list.')
            log.exception('Error retrieving games list')
        else:
            if len(games) == 0:
                self.onLogLine('No running games.')
            # TODO: display a dialog for user to select arena

            for game in games:
                joinSuccessful = yield self.attemptJoinGame(game)
                if joinSuccessful:
                    defer.returnValue(True)
                    return

        defer.returnValue(False)

    @defer.inlineCallbacks
    def attemptJoinGame(self, game):
        '''
        Attempts to join the given game.
        '''
        try:
            self.onLogLine('Found game: joining.')
            result = yield game.join(self)
        except authcommands.GameDoesNotExist:
            pass
        except AuthenticationCancelled:
            pass
        except authcommands.NotAuthenticated:
            self.onLogLine('Authentication failure.')
        except amp.UnknownRemoteError:
            self.onLogLine('Error on remote server.')
        except amp.RemoteAmpError as e:
            self.onLogLine('Error on remote server.')
            log.error('Remote error joining game: %s', e)
        except ConnectionFailed:
            self.onLogLine('Could not connect.')
        except ConnectionRefusedError:
            self.onLogLine('Connection refused.')
        except IOError as e:
            self.onLogLine('Error connecting to remote server.')
            log.error('Error attempting to join game: %s', e)
        except Exception:
            self.onLogLine('Error connecting to remote server.')
            log.exception('Error joining game.')
        else:
            self._joined(result)
            defer.returnValue(True)
            return

        defer.returnValue(False)

    def _joined(self, result):
        self.onSucceed.execute()
        self.app.connector.connectedToGame(*result)

    def cancel(self):
        self.cancelled = True
Exemplo n.º 24
0
class SoundSettingsTab(Tab, framework.CompoundElement):
    def __init__(self, app, onClose=None):
        super(SoundSettingsTab, self).__init__(app, 'Sounds')

        self.onClose = Event()
        if onClose is not None:
            self.onClose.addListener(onClose)

        font = self.app.screenManager.fonts.bigMenuFont
        colours = app.theme.colours

        text = [
            TextElement(self.app, 'Music Volume', font,
                        ScaledLocation(400, 285, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'Enable Music', font,
                        ScaledLocation(400, 355, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'Sound Volume', font,
                        ScaledLocation(400, 425, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'Enable Sound', font,
                        ScaledLocation(400, 495, 'topright'),
                        colours.headingColour),
        ]

        initVolume = app.soundSettings.musicVolume
        musicVolumeLabel = TextElement(self.app, '%d' % (initVolume, ), font,
                                       ScaledLocation(870, 280, 'topleft'),
                                       colours.headingColour)

        self.musicVolumeSlider = Slider(self.app,
                                        ScaledArea(450, 280, 400, 40))
        onSlide = lambda volume: musicVolumeLabel.setText('%d' % volume)
        self.musicVolumeSlider.onSlide.addListener(onSlide)
        self.musicVolumeSlider.onValueChanged.addListener(onSlide)
        self.musicVolumeSlider.setVal(initVolume)

        self.musicBox = CheckBox(self.app,
                                 ScaledLocation(450, 360),
                                 text='',
                                 font=font,
                                 colour=(192, 192, 192),
                                 initValue=app.soundSettings.musicEnabled)

        initSndVolume = app.soundSettings.soundVolume
        soundVolumeLabel = TextElement(self.app,
                                       '%d' % (initSndVolume, ), font,
                                       ScaledLocation(870, 420, 'topleft'),
                                       colours.headingColour)

        self.soundVolumeSlider = Slider(self.app,
                                        ScaledArea(450, 420, 400, 40))
        onSlide = lambda volume: soundVolumeLabel.setText('%d' % volume)
        self.soundVolumeSlider.onSlide.addListener(onSlide)
        self.soundVolumeSlider.onValueChanged.addListener(onSlide)
        self.soundVolumeSlider.setVal(initSndVolume)

        self.soundBox = CheckBox(self.app,
                                 ScaledLocation(450, 500),
                                 text='',
                                 font=font,
                                 colour=(192, 192, 192),
                                 initValue=app.soundSettings.soundEnabled)

        self.buttons = [
            button(app,
                   'save',
                   self.saveSettings, (-100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white),
            button(app,
                   'cancel',
                   self.onClose.execute, (100, -75),
                   'midbottom',
                   secondColour=app.theme.colours.white)
        ]

        self.elements = text + [
            musicVolumeLabel, self.musicVolumeSlider, self.musicBox,
            soundVolumeLabel, self.soundVolumeSlider, self.soundBox
        ] + self.buttons

    def saveSettings(self, sender=None):
        playMusic, volume, playSound, sndVolume = self.getValues()

        ss = self.app.soundSettings
        ss.musicEnabled = playMusic
        ss.musicVolume = volume
        ss.soundEnabled = playSound
        ss.soundVolume = sndVolume

        ss.save()
        ss.apply()

        self.onClose.execute()

    def getValues(self):

        playMusic = self.musicBox.value
        volume = self.musicVolumeSlider.getVal()

        playSound = self.soundBox.value
        sndVolume = self.soundVolumeSlider.getVal()

        return [playMusic, volume, playSound, sndVolume]
Exemplo n.º 25
0
class TrosnothServerFactory(MsgServer):
    messages = serverMsgs

    def __init__(self, game, authTagManager=None, *args, **kwargs):
        self.game = game
        self.authTagManager = authTagManager

        self.connectedClients = set()

        self.onShutdown = Event()  # ()

        self.running = True
        self._alreadyShutdown = False

    def checkGreeting(self, greeting):
        return (greeting == 'Trosnoth18')

    def startListening(self, port=6789, interface=''):
        try:
            self.port = reactor.listenTCP(port, self, interface=interface)
        except CannotListenError:
            log.warning('WARNING: Could not listen on port %s', port)
            self.port = reactor.listenTCP(0, self, interface=interface)

    def getTCPPort(self):
        return self.port.getHost().port

    def stopListening(self):
        self.port.stopListening()

    def gotBadString(self, protocol, data):
        log.warning('Server: Unrecognised network data: %r' % (data, ))
        log.warning('      : Did you invent a new network message and forget')
        log.warning('      : to add it to trosnoth.network.server.serverMsgs?')

    def connectionEstablished(self, protocol):
        '''
        Called by the network manager when a new incoming connection is
        completed.
        '''
        # Remember that this connection's ready for transmission.
        self.connectedClients.add(protocol)
        hub = LocalHub(self.game)
        hub.connectNode(protocol)

        # Send the setting information.
        protocol.gotServerCommand(InitClientMsg(self._getClientSettings()))

    def _getClientSettings(self):
        '''Returns a string representing the settings which must be sent to
        clients that connect to this server.'''

        result = self.game.world.dumpEverything()
        result['serverVersion'] = serverVersion

        return repr(result)

    def connectionLost(self, protocol, reason):
        if protocol in self.connectedClients:
            protocol.hub.disconnectNode()

        self.connectedClients.remove(protocol)

        # Check for game over and no connections left.
        if (len(self.connectedClients) == 0
                and self.game.world.uiOptions.showReadyStates):
            # Don't shut down if local player is connected.
            for p in self.game.world.players:
                if not p.bot:
                    break
            else:
                # Shut down the server.
                self.shutdown()

    def shutdown(self):
        if self._alreadyShutdown:
            return
        self._alreadyShutdown = True

        # Kill server
        self.running = False
        self.game.stop()
        self.onShutdown.execute()
Exemplo n.º 26
0
class ThemeTab(Tab, framework.TabFriendlyCompoundElement):
    def __init__(self, app, onClose=None, onRestart=None):
        super(ThemeTab, self).__init__(app, 'Themes')

        self.onClose = Event()
        if onClose is not None:
            self.onClose.addListener(onClose)
        self.onRestart = Event()
        if onRestart is not None:
            self.onRestart.addListener(onRestart)

        font = self.app.screenManager.fonts.menuFont
        colours = self.app.theme.colours

        self.inactiveTheme = False
        self.originalTheme = app.theme.name

        # Static text
        self.staticText = [
            TextElement(self.app, 'theme information:', font,
                        ScaledLocation(960, 250, 'topright'),
                        colours.headingColour),
            TextElement(self.app, 'theme contents:', font,
                        ScaledLocation(960, 390, 'topright'),
                        colours.headingColour)
        ]

        # Dynamic text
        self.feedbackText = TextElement(
            self.app, 'Your current theme: %s' % (app.theme.name, ), font,
            ScaledLocation(512, 200, 'midtop'), colours.startButton)
        self.listHeaderText = TextElement(self.app, 'available themes:', font,
                                          ScaledLocation(70, 250),
                                          colours.headingColour)
        self.themeNameText = TextElement(self.app, 'Default Theme', font,
                                         ScaledLocation(960, 290, 'topright'),
                                         colours.startButton)
        self.themeAuthorText = TextElement(
            self.app, 'created by: Trosnoth Team', font,
            ScaledLocation(960, 330, 'topright'), colours.startButton)

        self.contents = []

        numContents = 4
        for yPos in range(430, 430 + numContents * 40 + 1, 40):
            self.contents.append(
                TextElement(self.app, '', font,
                            ScaledLocation(960, yPos, 'topright'),
                            colours.startButton))

        self.dynamicText = [
            self.feedbackText, self.listHeaderText, self.themeNameText,
            self.themeAuthorText
        ] + self.contents

        # Theme list
        self.themeList = ListBox(self.app, ScaledArea(70, 290, 400, 290), [],
                                 font, colours.listboxButtons)
        self.themeList.onValueChanged.addListener(self.updateSidebar)

        # Text buttons
        self.useThemeButton = button(app, 'use selected theme',
                                     self.applyTheme, (0, -125), 'midbottom')
        self.refreshButton = button(app, 'refresh', self.populateList,
                                    (-100, -75), 'midbottom')
        self.cancelButton = button(app, 'cancel', self.backToMain, (100, -75),
                                   'midbottom')
        self.restartButton = button(app, 'restart Trosnoth', self.restart,
                                    (0, -125), 'midbottom')

        self.buttons = [
            self.useThemeButton, self.refreshButton, self.cancelButton,
            self.restartButton
        ]

        # Combine the elements
        self.elements = self.staticText + self.dynamicText + self.buttons + [
            self.themeList
        ]

        self.contentTypes = {
            "sprites": "sprite",
            "blocks": "map block",
            "fonts": "font",
            "startupMenu": "backdrop"
        }

        # Populate the list of replays
        self.populateList()

    def populateList(self):

        defaultTheme = {
            "name": "Default Theme",
            "filename": "default",
            "author": "Trosnoth Team",
            "content": None,
            "source": "internal"
        }

        # Clear out the sidebar
        self.themeNameText.setText('')
        self.themeAuthorText.setText('')
        for element in self.contents:
            element.setText('')
        self.useThemeButton.setText('')
        self.restartButton.setText('')
        self.listHeaderText.setText('available themes:')
        self.themeList.index = -1

        userThemeDir = getPath(user, 'themes')
        internalThemeDir = getPath(themes)
        makeDirs(userThemeDir)

        themeList = []

        # Get a list of internal themes
        for dirName in os.listdir(internalThemeDir):
            if os.path.isdir(os.path.join(internalThemeDir, dirName)):
                themeList.append("i/%s" % dirName)

        # Get a list of user-defined themes
        for dirName in os.listdir(userThemeDir):
            if os.path.isdir(os.path.join(userThemeDir, dirName)):
                # Internal themes overrule user-defined themes
                if "i/" + dirName not in themeList and dirName != "default":
                    themeList.append("u/%s" % dirName)

        # Assume all themes are valid for now
        validThemes = themeList[:]

        self.themeInfo = {}

        for themeName in themeList:
            themeInfo = {"content": {}}

            if themeName.startswith("i/"):
                themeInfo['source'] = 'internal'
                directory = internalThemeDir
            else:
                themeInfo['source'] = 'user-defined'
                directory = userThemeDir

            themeNameList = themeName
            themeName = themeName[2:]

            themeInfo['filename'] = themeName[2:]

            anyContent = False

            for contentType in list(self.contentTypes.keys()):
                if themeInfo['source'] == 'internal':
                    contentDir = os.path.join(directory, themeName,
                                              contentType)
                else:
                    contentDir = os.path.join(directory, themeName,
                                              contentType)

                if not os.path.isdir(contentDir):
                    continue
                else:
                    fileCount = len([
                        f for f in os.listdir(contentDir)
                        if os.path.isfile(os.path.join(contentDir, f))
                    ])
                    if fileCount > 0:
                        anyContent = True
                        themeInfo["content"][contentType] = fileCount

            if not anyContent:
                validThemes.remove(themeNameList)
                continue

            infoFile = os.path.join(directory, themeName, "info.txt")
            if os.path.isfile(infoFile):
                infoFile = open(infoFile)
                infoContents = infoFile.readlines()
            else:
                infoContents = []

            if len(infoContents) >= 2:
                themeInfo["author"] = infoContents[1].strip()
            else:
                themeInfo["author"] = None

            if len(infoContents) >= 1:
                themeInfo["name"] = infoContents[0].strip()
            else:
                themeInfo["name"] = themeName

            self.themeInfo[themeName] = themeInfo

        self.themeInfo["default"] = defaultTheme

        # Sort the themes alphabetically
        items = [(v['filename'], n) for n, v in self.themeInfo.items()]
        items.sort()
        items = [n for v, n in items]
        self.themeList.setItems(items)

        if len(self.themeInfo) == 1:
            self.listHeaderText.setText("1 available theme:")
            self.themeList.index = 0
            self.updateSidebar(0)
        else:
            self.listHeaderText.setText("%d available themes:" %
                                        len(self.themeInfo))

    def updateSidebar(self, listID):
        # Update the details on the sidebar
        displayName = self.themeList.getItem(listID)

        themeInfo = self.themeInfo[displayName]

        if themeInfo['source'] == "internal":
            self.themeNameText.setText(themeInfo['name'] + " (built-in)")
        else:
            self.themeNameText.setText(themeInfo['name'])
        if themeInfo['author'] is not None:
            self.themeAuthorText.setText("by " + themeInfo['author'])
        else:
            self.themeAuthorText.setText("(creator unknown)")

        if themeInfo['content'] is None:
            for element in self.contents:
                element.setText('')
            self.contents[0].setText('N/A')
        else:
            count = 0
            for k, v in themeInfo['content'].items():
                suffix = ""
                if v != 1:
                    suffix = "s"

                text = "%d %s%s" % (v, self.contentTypes[k], suffix)
                self.contents[count].setText(text)
                count += 1

        self.restartButton.setText("")
        if self.app.displaySettings.theme != displayName:
            if displayName == self.originalTheme:
                self.useThemeButton.setText("revert back to this theme")
            else:
                self.useThemeButton.setText("use this theme")
        else:
            self.useThemeButton.setText("")

            if self.inactiveTheme:
                self.restartButton.setText('restart Trosnoth')

    def applyTheme(self):
        themeName = self.themeList.getItem(self.themeList.index)

        self.app.displaySettings.theme = themeName
        self.app.displaySettings.save()

        self.useThemeButton.setText("")

        if themeName != self.originalTheme:
            self.feedbackText.setText("You must restart Trosnoth for the "
                                      "'%s' theme to take effect." % themeName)
            self.restartButton.setText('restart Trosnoth')
            self.inactiveTheme = True
        else:
            self.feedbackText.setText('Your current theme: %s' %
                                      (self.app.theme.name, ))
            self.restartButton.setText("")
            self.inactiveTheme = False

    def restart(self):
        self.onRestart.execute()

    def backToMain(self):
        if self.inactiveTheme:
            self.feedbackText.setText("Your current theme: %s "
                                      "(restart required)" %
                                      self.app.displaySettings.theme)

        self.onClose.execute()

    def draw(self, surface):
        super(ThemeTab, self).draw(surface)
        width = max(int(3 * self.app.screenManager.scaleFactor), 1)
        scalePoint = self.app.screenManager.placePoint
        pygame.draw.line(surface, self.app.theme.colours.mainMenuColour,
                         scalePoint((512, 260)), scalePoint((512, 580)), width)
Exemplo n.º 27
0
class InputBox(framework.Element):
    pxFromLeft = 2
    pxFromRight = 5
    pxToSkipWhenCursorGoesToTheRight = 90
    cursorFlashTime = 0.6

    def __init__(self,
                 app,
                 area,
                 initValue='',
                 validator=None,
                 font=None,
                 colour=(255, 255, 255),
                 maxLength=None,
                 onClick=None,
                 onEnter=None,
                 onTab=None,
                 onEsc=None,
                 onEdit=None):
        super(InputBox, self).__init__(app)

        self.area = area
        self.onClick = Event()
        if onClick is not None:
            self.onClick.addListener(onClick)
        self.onEnter = Event()
        if onEnter is not None:
            self.onEnter.addListener(onEnter)
        self.onTab = Event()
        if onTab is not None:
            self.onTab.addListener(onTab)
        self.onEsc = Event()
        if onEsc is not None:
            self.onEsc.addListener(onEsc)
        self.onEdit = Event()
        if onEdit is not None:
            self.onEdit.addListener(onEdit)

        if validator:
            self.validator = validator
        else:
            self.validator = lambda x: True

        if font:
            self.font = font
        else:
            self.font = self.app.screenManager.fonts.defaultTextBoxFont

        self.value = initValue
        self.maxLength = maxLength
        self.fontColour = (0, 0, 0)
        self.backColour = colour
        self.cursorColour = (255, 0, 0)
        self._cursorVisible = True
        self._timeToFlash = self.cursorFlashTime

        # Calculate the white part of the input area
        self.cursorIndex = len(self.value)
        self.offset = 0

    def clear(self):
        self.setValue('')

    def setValue(self, value):
        self.value = value
        self.offset = 0
        self.cursorIndex = len(self.value)

    def getValue(self):
        return self.value

    def setFont(self, font):
        self.font = font

    def setValidator(self, validator):
        self.validator = validator

    def setMaxLength(self, length):
        self.maxLength = length

    def _getSize(self):
        return self._getRect().size

    def _getCursorImage(self):
        s = pygame.Surface((2,
                            min(int(self.font.getHeight(self.app)),
                                self._getSize()[1] * 0.8)))
        s.fill(self.cursorColour)
        return s

    def _getRect(self):
        return self.area.getRect(self.app)

    def _getPt(self):
        return self._getRect().topleft

    def setFontColour(self, fontColour):
        self.fontColour = fontColour

    def setBackColour(self, backColour):
        self.backColour = backColour

    def setCursorColour(self, cursorColour):
        self.cursorColour = cursorColour

    def gotFocus(self):
        self._cursorVisible = True
        self._timeToFlash = self.cursorFlashTime

    def _renderText(self):
        '''
        Provided so that PasswordBox can override it.
        '''
        return self.font.render(self.app, self.value, True, self.fontColour,
                                self.backColour)

    def draw(self, surface):
        rect = self._getRect()
        size = rect.size
        pos = rect.topleft
        # Fill the input area with the specified colour
        surface.fill(self.backColour, rect)

        # Put what's currently inputted into the input area
        inputText = self._renderText()

        # Adjust offset
        cursorPos = self._getCursorPos()
        if cursorPos < self.offset:
            self.offset = max(
                0, self.offset - self.pxToSkipWhenCursorGoesToTheRight)
        else:
            minOffset = cursorPos - size[0] + self.pxFromRight + self.pxFromLeft
            if self.offset < minOffset:
                self.offset = minOffset

        text_rect = inputText.get_rect()
        text_rect.centery = rect.centery
        diff = (text_rect.height - rect.height) / 2
        # Find the currently-displaying text (based on where the cursor is):
        area = pygame.Rect(self.offset, diff, size[0] - self.pxFromRight,
                           rect.height)

        # Put the text on the screen
        surface.blit(inputText, (pos[0] + self.pxFromLeft, rect.top), area)

        # Add the cursor where it is.
        if self.hasFocus and self._cursorVisible:
            cs = self._getCursorImage()
            cursor_rect = cs.get_rect()
            cursor_rect.centery = rect.centery
            surface.blit(
                cs,
                (pos[0] + self._getCursorPos() - self.offset, cursor_rect.top))

    def _getCursorPos(self):
        return self._getFontSize(self.value[:self.cursorIndex])[0] + 1

    def _getFontSize(self, text):
        '''
        Provided so that PasswordBox can override it.
        '''
        return self.font.size(self.app, text)

    # Get the index of the position clicked
    def __getCursorIndex(self, clickOffset):
        totalOffset = clickOffset + self.offset + self.pxFromLeft
        i = 1
        fontOffset = 0
        last = 0
        while fontOffset < totalOffset and i <= len(self.value):
            last = fontOffset
            fontOffset = self._getFontSize(self.value[:i])[0]
            i += 1
        if (fontOffset - totalOffset) <= (totalOffset - last):
            return i - 1
        else:
            return i - 2
        return i

    def processEvent(self, event):
        '''Processes the key press. If we use the event, return nothing.
        If we do not use it,
        return the event so something else can use it.'''

        if self.hasFocus and event.type == pygame.KEYDOWN:
            self._cursorVisible = True
            self._timeToFlash = self.cursorFlashTime
            if event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
                self.onEnter.execute(self)
                return None
            if event.key == pygame.K_ESCAPE:
                self.onEsc.execute(self)
                return None
            if event.key == pygame.K_TAB:
                self.onTab.execute(self)
                return None

            # Delete the letter behind the cursor
            if event.key == pygame.K_BACKSPACE:
                # Make sure there's something in the string behind the cursor
                # first, don't want to kill what's not there
                if self.cursorIndex > 0:
                    self.value = (self.value[:self.cursorIndex - 1] +
                                  self.value[self.cursorIndex:])
                    self.cursorIndex -= 1
                    self.onEdit.execute(self)

            elif event.key == pygame.K_DELETE:
                # Make sure there's something in the string in front of the
                # cursor first, don't want to kill what's not there
                if self.cursorIndex < len(self.value):
                    self.value = (self.value[:self.cursorIndex] +
                                  self.value[self.cursorIndex + 1:])
                    self.onEdit.execute(self)

            elif event.key == pygame.K_LEFT:
                if self.cursorIndex > 0:
                    self.cursorIndex -= 1

            elif event.key == pygame.K_RIGHT:
                if self.cursorIndex < len(self.value):
                    self.cursorIndex += 1

            elif event.key == pygame.K_END:
                self.cursorIndex = len(self.value)

            elif event.key == pygame.K_HOME:
                self.cursorIndex = 0
                self.offset = 0

            # Add the character to our string
            elif event.unicode == '':
                return event
            else:
                # Validate the input.
                if not self.validator(event.unicode):
                    return event

                # Check the maxLength
                if self.maxLength is not None:
                    if len(self.value) >= self.maxLength:
                        return event

                # Add the typed letter to the string
                self.value = (self.value[:self.cursorIndex] + event.unicode +
                              self.value[self.cursorIndex:])
                self.cursorIndex += len(event.unicode)
                self.onEdit.execute(self)

        else:
            rect = self._getRect()

            if (event.type == pygame.MOUSEBUTTONDOWN
                    and rect.collidepoint(event.pos) and event.button == 1):

                self.onClick.execute(self)
                xOffset = event.pos[0] - rect.left
                self.cursorIndex = self.__getCursorIndex(xOffset)
                self._timeToFlash = self.cursorFlashTime
                self._cursorVisible = True
            else:
                # It's not a keydown event. Pass it on.
                return event

    def tick(self, deltaT):
        self._timeToFlash -= deltaT
        while self._timeToFlash < 0:
            self._timeToFlash += self.cursorFlashTime
            self._cursorVisible = not self._cursorVisible
Exemplo n.º 28
0
class Slider(framework.Element):
    def __init__(self, app, area, bounds=None, snap=False):
        super(Slider, self).__init__(app)
        self.app = app
        self.area = area
        self.beingDragged = False
        self.snap = snap
        if not bounds:
            bounds = 0, 100
        self.lowBound, self.highBound = bounds
        self.unscaledSlider = 20
        self.sliderRectWidth = int(self.unscaledSlider *
                                   app.screenManager.scaleFactor)
        self.lineColour = (0, 192, 0)
        self.sliderColour = (0, 255, 0)
        self.onValueChanged = Event()
        self.onSlide = Event()

        self.largeArea = ScaledArea(
            self.area.point.val[0] + self.unscaledSlider / 2,
            self.area.point.val[1],
            self.area.size.val[0] - self.unscaledSlider + 1,
            self.area.size.val[1])

        self.area, self.largeArea = self.largeArea, self.area

        self.sliderVal = self.highBound

    def doSnap(self, value):
        if self.snap:
            return int(value + 0.5)
        return value

    def getVal(self):
        return self.sliderVal

    def setVal(self, val):
        old = self.sliderVal
        self.sliderVal = self.doSnap(val)
        if old != self.sliderVal:
            self.onValueChanged.execute(self.sliderVal)

    def setRange(self, low, high):
        self.lowBound = low
        self.highBound = high
        if self.sliderVal > self.highBound:
            self.sliderVal = self.highBound
        elif self.sliderVal < self.lowBound:
            self.sliderVal = self.lowBound

    def setSliderColour(self, colour):
        self.sliderColour = colour

    def setLineColour(self, colour):
        self.lineColour = colour

    def _getRange(self):
        return self.highBound - self.lowBound

    def _getRect(self):
        return self.area.getRect(self.app)

    def _getPt(self):
        return self._getRect().topleft

    def _getSize(self):
        return self._getRect().size

    def _valPerPixel(self):
        return self._getSize()[0] / (self._getRange() + 0.)

    def _pixelPerVal(self):
        return self._getRange() / (self._getSize()[0] + 0.)

    # Number of pixels from 0 to width
    def _relPx(self, x):
        return min(max(0, (x - self._getPt()[0])), self._getSize()[0])

    def _mouseAt(self, x):
        old = self.sliderVal
        self.sliderVal = self.lowBound + self._pixelPerVal() * self._relPx(x)
        if old != self.sliderVal:
            self.onSlide.execute(self.sliderVal)

    def _getSliderRect(self):
        # Ignore pos for now
        sliderRect = pygame.Rect(0, 0, self._getSliderRectWidth(),
                                 self._getSize()[1])
        pt = self._getPt()
        sliderRect.midtop = (
            (self.sliderVal - self.lowBound) * self._valPerPixel() + pt[0],
            pt[1])
        return sliderRect

    def _getLineWidth(self):
        return max(1, self._getSize()[1] / 8)

    def _getSliderRectWidth(self):
        return int(self.unscaledSlider * self.app.screenManager.scaleFactor)

    def processEvent(self, event):
        if (event.type == pygame.MOUSEBUTTONDOWN
                and self.largeArea.getRect(self.app).collidepoint(event.pos)):
            # Each click of the scroll wheel moves the slider by 5%
            percent = int((self.highBound - self.lowBound) * 0.05)

            if event.button == 1:
                self.beingDragged = True
                self._mouseAt(event.pos[0])
            elif event.button == 4:
                self.setVal(min(self.highBound, self.sliderVal + percent))
            elif event.button == 5:
                self.setVal(max(self.lowBound, self.sliderVal - percent))
            else:
                return event

        elif event.type == pygame.MOUSEMOTION and self.beingDragged:
            self._mouseAt(event.pos[0])

        elif (event.type == pygame.MOUSEBUTTONUP and event.button == 1
              and self.beingDragged):
            self.beingDragged = False
            self.sliderVal = self.doSnap(self.sliderVal)
            self.onValueChanged.execute(self.sliderVal)
        else:
            return event
        return None

    def draw(self, surface):
        r = self.largeArea.getRect(self.app)
        lineWidth = self._getLineWidth()
        pygame.draw.line(surface, self.lineColour, r.midleft, r.midright,
                         lineWidth)

        if self.snap:
            low = r.left
            high = r.right
            count = self.highBound - self.lowBound + 0.
            y0 = r.centery - 0.25 * r.height
            y1 = r.centery + 0.25 * r.height
            for i in xrange(self.lowBound, self.highBound + 1):
                x = (high * i + low * (count - i)) / count
                pygame.draw.line(surface, self.lineColour, (x, y0), (x, y1),
                                 lineWidth)

        surface.fill(self.sliderColour, self._getSliderRect())
Exemplo n.º 29
0
class KeycodeBox(framework.Element):
    pxFromLeft = 2
    pxFromRight = 5

    def __init__(self,
                 app,
                 area,
                 initValue=None,
                 font=None,
                 colour=(255, 255, 255),
                 focusColour=(192, 192, 255),
                 acceptMouse=False):
        super(KeycodeBox, self).__init__(app)

        self.area = area
        self.onClick = Event()
        self.onChange = Event()

        if font:
            self.font = font
        else:
            self.font = self.app.screenManager.fonts.defaultTextBoxFont

        self.value = initValue
        self.fontColour = (0, 0, 0)
        self.realBackColour = self._backColour = colour
        self.focusColour = focusColour
        self.acceptMouse = acceptMouse

    def _getBackColour(self):
        return self._backColour

    def _setBackColour(self, colour):
        if self.realBackColour == self._backColour:
            self.realBackColour = colour
        self._backColour = colour

    backColour = property(_getBackColour, _setBackColour)

    def draw(self, surface):
        rect = self._getRect()
        size = rect.size
        pos = rect.topleft

        # Fill the input area with the specified colour
        surface.fill(self.realBackColour, rect)

        # Put what's currently inputted into the input area
        if self.value is None:
            name = ''
        else:
            name = keyboard.shortcutName(self.value)

        inputText = self.font.render(self.app, name, True, self.fontColour,
                                     self.realBackColour)

        text_rect = inputText.get_rect()

        diff = (text_rect.height - rect.height) / 2
        area = pygame.Rect(0, diff, size[0] - self.pxFromRight, rect.height)

        # Put the text on the screen
        text_rect.centery = rect.centery
        surface.blit(inputText, (pos[0] + self.pxFromLeft, rect.top), area)

    def processEvent(self, event):
        '''Processes the key press. If we use the event, return nothing.
        If we do not use it,
        return the event so something else can use it.'''

        if self.hasFocus:
            # Catch a single keystroke.
            if event.type == pygame.KEYDOWN:
                self.caughtKey(event.key)
            elif self.acceptMouse and event.type == pygame.MOUSEBUTTONDOWN:
                if event.button != 1:
                    self.caughtKey(keyboard.mouseButton(event.button))
        else:
            rect = self._getRect()

            if (event.type == pygame.MOUSEBUTTONDOWN
                    and rect.collidepoint(event.pos)):
                self.onClick.execute(self)
            else:
                # It's not a keydown event. Pass it on.
                return event

    def caughtKey(self, key):
        self.value = key
        self.hasFocus = False
        self.lostFocus()
        self.onChange.execute(self)

    def _getSize(self):
        return self._getRect().size

    def _getRect(self):
        return self.area.getRect(self.app)

    def _getPt(self):
        return self._getRect().topleft

    def gotFocus(self):
        self.realBackColour = self.focusColour

    def lostFocus(self):
        self.realBackColour = self._backColour
Exemplo n.º 30
0
class PlayAuthScreen(framework.CompoundElement):
    passwordGUIFactory = PasswordGUI

    def __init__(self, app, onSucceed=None, onFail=None):
        super(PlayAuthScreen, self).__init__(app)
        self.onSucceed = Event(listener=onSucceed)
        self.onFail = Event(listener=onFail)
        self.lobby = None
        self.badServers = set()

        if app.displaySettings.alphaOverlays:
            alpha = 192
        else:
            alpha = None
        bg = SolidRect(app, app.theme.colours.playMenu, alpha,
                       Region(centre=Canvas(512, 384), size=Canvas(924, 500)))

        colour = app.theme.colours.mainMenuColour
        font = app.screenManager.fonts.consoleFont
        self.logBox = LogBox(
            app, Region(size=Canvas(900, 425), midtop=Canvas(512, 146)),
            colour, font)

        font = app.screenManager.fonts.bigMenuFont
        cancel = TextButton(app,
                            Location(Canvas(512, 624), 'midbottom'),
                            'Cancel',
                            font,
                            app.theme.colours.secondMenuColour,
                            app.theme.colours.white,
                            onClick=self.cancel)
        self.cancelled = False
        self.elements = [bg, self.logBox, cancel]
        self._passwordGetter = None

    @property
    def passwordGetter(self):
        if self._passwordGetter is None:
            self._passwordGetter = self.passwordGUIFactory(self.app)
        return self._passwordGetter

    @defer.inlineCallbacks
    def begin(self, servers, canHost=True):
        self.cancelled = False
        self.passwordGetter.cancelled = False
        self.badServers = set()
        if self.lobby is None:
            self.lobby = Lobby(self.app)

        # Removes the third item (http) from the tuple since we don't care about
        # it.
        servers = [(server[:2] if isinstance(server, tuple) else server)
                   for server in servers]

        for server in servers:
            if self.cancelled:
                break

            if server == 'self':
                if self.app.server is not None:
                    self.onSucceed.execute()
                    self.app.interface.connectToLocalServer()
                    return

            elif isinstance(server, tuple):
                if server in self.badServers:
                    continue

                self.logBox.log('Requesting games from %s:%d...' % server)
                connected = yield self.attemptServerConnect(
                    self.lobby.getGames, server)
                if connected:
                    return

            elif server == 'others':
                for server in servers:
                    if (server in self.badServers
                            or not isinstance(server, tuple)):
                        continue

                    self.logBox.log('Asking %s:%d about other games...' %
                                    server)
                    connected = yield self.attemptServerConnect(
                        self.lobby.getOtherGames, server)
                    if connected:
                        return

            elif server == 'lan':
                self.logBox.log('Asking local network for other games...')
                games = yield self.lobby.getMulticastGames()
                for game in games:
                    joinSuccessful = yield self.attemptJoinGame(game)
                    if joinSuccessful:
                        return

            elif server == 'create':
                for server in servers:
                    if server in self.badServers or not isinstance(
                            server, tuple):
                        continue
                    self.logBox.log('Asking to create game on %s...' %
                                    (server[0], ))

                    joinSuccessful = yield self.attemptCreateGame(server)
                    if joinSuccessful:
                        return

        if canHost:
            if not self.cancelled:
                result = yield HostGameQuery(self.app).run()

                if not result:
                    self.onFail.execute()
                    return

                self.app.startServer(2, 1)

                # Notify remaining auth servers of this game.
                for server in servers:
                    if server in self.badServers or not isinstance(
                            server, tuple):
                        continue
                    self.logBox.log('Registering game with %s...' %
                                    (server[0], ))
                    try:
                        result = yield self.lobby.registerGame(
                            server, self.app.server)
                    except ConnectError:
                        self.logBox.log('Unable to connect.')
                    if not result:
                        self.logBox.log('Registration failed.')

                self.onSucceed.execute()
                self.app.interface.connectToLocalServer()
        else:
            if not self.cancelled:
                box = OkBox(self.app, ScaledSize(450, 150), 'Trosnoth',
                            'Connection unsuccessful.')
                box.onClose.addListener(self.onFail.execute)
                box.show()

    @defer.inlineCallbacks
    def attemptServerConnect(self, getGamesList, server):
        '''
        Attempts to connect to a game on the server, returned by
        getGamesList(server).
        '''
        try:
            games = yield getGamesList(server)
        except ConnectError:
            self.logBox.log('Unable to connect.')
            self.badServers.add(server)
        except amp.UnknownRemoteError:
            self.logBox.log('Error on remote server.')
        except amp.RemoteAmpError, e:
            self.logBox.log('Error on remote server.')
            log.exception(str(e))
        except IOError, e:
            self.logBox.log('Error connecting to remote server.')
            log.exception(str(e))
            self.badServers.add(server)