예제 #1
0
    def new_zone(self, areas: Set[AreaManager.Area],
                 watchers: Set[ClientManager.Client]) -> str:
        """
        Create a zone with the given area set and given watchers set and return its ID.

        Parameters
        ----------
        areas: set of AreaManager.Area
            Set of areas the zone covers.
        watchers: set of ClientManager.Client
            Set of clients who are watching the zone.

        Returns
        -------
        str
            The ID of the zone just created.

        Raises
        ------
        ZoneError.AreaConflictError:
            If one of the areas of the new zone already belongs to some other zone.
        ZoneError.WatcherConflictError:
            If one of the watchers of the new zone is already watching some other zone.
        """

        zone_id = self._generate_id()
        conflict_areas = self.areas_in_some_zone(areas)
        if conflict_areas:
            if len(conflict_areas) == 1:
                message = 'Area {} already belongs in a zone.'.format(
                    conflict_areas.pop())
            else:
                message = ('Areas {} already belong in a zone.'.format(
                    Constants.cjoin([area.id for area in conflict_areas])))
            raise ZoneError.AreaConflictError(message)

        conflict_watchers = self.watchers_in_some_zone(watchers)
        if conflict_watchers:
            if len(conflict_watchers) == 1:
                message = 'Watcher {} is already watching a zone.'.format(
                    conflict_watchers.pop())
            else:
                message = ('Watchers {} are already watching a zone.'.format(
                    Constants.cjoin(conflict_watchers)))
            raise ZoneError.WatcherConflictError(message)

        zone = self.Zone(self._server, zone_id, areas, watchers)
        self._zones[zone_id] = zone
        self._check_structure()
        return zone_id
예제 #2
0
        def get_info(self) -> str:
            """
            Obtain the zone details (ID, areas, watchers) as a human readable string.

            Returns
            -------
            str:
                Zone details in human readable format.
            """

            # Format areas
            area_description = Constants.format_area_ranges(self._areas)

            # Obtain watchers
            watchers = sorted(self._watchers)
            if watchers:
                watcher_infos = [
                    '[{}] {} ({})'.format(c.id, c.displayname, c.area.id)
                    for c in watchers
                ]
                watcher_description = Constants.cjoin(watcher_infos)
            else:
                watcher_description = 'None'

            # Obtain players
            player_description = len(self._players)

            return (
                'Zone {}. Contains areas: {}. Is watched by: {}. Players in zone: {}.'
                .format(self._zone_id, area_description, watcher_description,
                        player_description))
예제 #3
0
        def get_info(self):
            """
            Obtain the zone details (ID, areas, watchers) as a human readable string.

            Returns
            -------
            str:
                Zone details in human readable format.
            """

            # Obtain area ranges
            raw_area_ids = sorted([area.id for area in self._areas])
            last_area = raw_area_ids[0]
            area_ranges = list()
            current_range = [last_area, last_area]

            def add_range():
                if current_range[0] != current_range[1]:
                    area_ranges.append('{}-{}'.format(current_range[0],
                                                      current_range[1]))
                else:
                    area_ranges.append('{}'.format(current_range[0]))

            for area_id in raw_area_ids[1:]:
                if area_id != last_area + 1:
                    add_range()
                    current_range = [area_id, area_id]
                else:
                    current_range[1] = area_id
                last_area = area_id

            add_range()
            area_description = Constants.cjoin(area_ranges)

            # Obtain watchers
            watcher_infos = [
                '{} ({})'.format(c.displayname, c.area.id)
                for c in self._watchers
            ]
            watcher_description = Constants.cjoin(watcher_infos)

            return ('Zone {}. Contains areas: {}. Is watched by: {}.'.format(
                self._zone_id, area_description, watcher_description))
예제 #4
0
    def notify_me_blood(self,
                        area,
                        changed_visibility=True,
                        changed_hearing=True):
        client = self.client
        changed_area = (client.area != area)

        ###########
        # If someone else is bleeding in the new area, notify the person moving
        bleeding_visible = [
            c for c in area.clients
            if c.is_visible and c.is_bleeding and c != client
        ]
        bleeding_sneaking = [
            c for c in area.clients
            if not c.is_visible and c.is_bleeding and c != client
        ]
        info = ''
        vis_info = ''
        sne_info = ''

        # To prepare message with players bleeding, one of these must be true:
        # 1. You are staff
        # 2. Lights are on and you are not blind
        # Otherwise, prepare faint drops of blood if you are not deaf.
        # Otherwise, just prepare 'smell' if lights turned off or you are blind

        if bleeding_visible:
            normal_visibility = changed_visibility and area.lights and not client.is_blind
            if client.is_staff() or normal_visibility:
                vis_info = ('{}You see {} {} bleeding'.format(
                    '(X) ' if not normal_visibility else '',
                    Constants.cjoin([c.displayname for c in bleeding_visible]),
                    'is' if len(bleeding_visible) == 1 else 'are'))
            elif not client.is_deaf and changed_hearing:
                vis_info = 'You hear faint drops of blood'
            elif client.is_blind and client.is_deaf and changed_area:
                vis_info = 'You smell blood'

        # To prepare message with sneaked bleeding, you must be staff.
        # Otherwise, prepare faint drops of blood if you are not deaf.
        # Otherwise, just prepare 'smell' if lights turned off or you are blind

        if bleeding_sneaking:
            if client.is_staff():
                sne_info = ('(X) You see {} {} bleeding while sneaking'.format(
                    Constants.cjoin([c.displayname
                                     for c in bleeding_sneaking]),
                    'is' if len(bleeding_visible) == 1 else 'are'))
            elif not client.is_deaf and changed_hearing:
                sne_info = 'You hear faint drops of blood'
            elif not area.lights or client.is_blind and changed_area:
                sne_info = 'You smell blood'

        # If there is visible info, merge it with sneak info if the following is true
        # 1. There is sneak info
        # 2. Sneak info is not 'You smell blood' (as that would be true anyway)
        # 3. It is not the same as the visible info (To avoid double 'hear faint drops')
        if vis_info:
            if sne_info and sne_info != 'You smell blood' and vis_info != sne_info:
                info = '{}, and {}'.format(info, sne_info.lower())
            else:
                info = vis_info
        else:
            info = sne_info

        if info:
            client.send_ooc(info + '.')

        ###########
        # If there are blood trails in the area, send notification if one of the following is true
        ## 1. You are staff
        ## 2. Lights are on and you are not blind.
        ## If the blood in the area is smeared, just indicate there is smeared blood for non-staff
        ## and the regular blood trail message with extra text for staff.
        # If neither is true, send 'smell' notification as long as the following is true:
        # 1. Lights turned off or you are blind
        # 2. A notification was not sent in the previous part

        normal_visibility = changed_visibility and area.lights and not client.is_blind
        if client.is_staff() or normal_visibility:
            start_connector = '(X) ' if not normal_visibility else ''
            smeared_connector = 'smeared ' if client.is_staff(
            ) and area.blood_smeared else ''

            if not client.is_staff() and area.blood_smeared:
                client.send_ooc(
                    '{}You spot some smeared blood in the area.'.format(
                        start_connector))
            elif area.bleeds_to == set([area.name]):
                client.send_ooc('{}You spot some {}blood in the area.'.format(
                    start_connector, smeared_connector))
            elif len(area.bleeds_to) > 1:
                bleed_to_areas = list(area.bleeds_to - set([area.name]))
                if client.is_staff() and area.blood_smeared:
                    start_connector = '(X) '  # Force staff indication

                info = ('{}You spot a {}blood trail leading to {}.'.format(
                    start_connector, smeared_connector,
                    Constants.cjoin(bleed_to_areas, the=True)))
                client.send_ooc(info)
        elif not client.is_staff() and (area.bleeds_to or
                                        area.blood_smeared) and changed_area:
            if not info:
                client.send_ooc('You smell blood.')
예제 #5
0
    def notify_me_status(self, area: AreaManager.Area, changed_visibility: bool = True,
                         changed_hearing: bool = True) -> bool:
        client = self.client
        normal_visibility = changed_visibility and area.lights and not client.is_blind
        info = ''
        vis_info = ''
        sne_info = ''
        # While we always notify in OOC if someone has a custom status, we only ping IC if the
        # status has changed from the last time the player has seen it.
        # This prevents ping spam if two players with custom statuses move together.
        found_something = False

        status_visible = [c for c in area.clients if c.status and c != client and c.is_visible]
        status_sneaking = [c for c in area.clients if c.status and c != client and not c.is_visible]
        staff_privileged = not normal_visibility
        if status_visible:
            if client.is_staff() or normal_visibility:
                mark = '(X) ' if staff_privileged else ''
                players = Constants.cjoin([c.displayname for c in status_visible])
                verb = 'was' if len(status_visible) == 1 else 'were'
                vis_info = (f'{mark}You note something about {players} who {verb} in the area '
                            f'already')

                for player in status_visible:
                    remembered_status = client.remembered_statuses.get(player.id, '')
                    if player.status != remembered_status:
                        # Found someone whose status has changed
                        # Only for these situations do we want to ping
                        found_something = True
                    client.remembered_statuses[player.id] = player.status

            elif changed_visibility and not client.is_deaf:
                # Give nerfed notifications if the lights are out or the player is blind, but NOT
                # if the player is deaf.
                vis_info = 'You think something is unusual about someone in the area'

        # To prepare message with sneaked bleeding, you must be staff.
        # Otherwise, prepare faint drops of blood if you are not deaf.
        # Otherwise, just prepare 'smell' if lights turned off or you are blind

        if status_sneaking:
            if client.is_staff():
                players = Constants.cjoin([c.displayname for c in status_sneaking])
                verb = 'was' if len(status_sneaking) == 1 else 'were'
                sne_info = (f'(X) You note something about {players}, who {verb} in the area '
                            f'already and also sneaking')

        if vis_info and sne_info:
            # Remove marks and capital letters. Use 'count=1' explicitly to prevent more
            # replacements that could happen with player displaynames.
            # Then readd the mark manually (mark would have been present because sne_info)
            vis_info = vis_info[4:] if vis_info.startswith('(X)') else vis_info
            sne_info = sne_info.replace("(X) You", "you", 1)
            info = f'(X) {vis_info}, and {sne_info}'
        elif vis_info:
            info = vis_info
        elif sne_info:
            info = sne_info

        if info:
            client.send_ooc(info + '.')

        return found_something