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
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))
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))
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.')
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