class AmbientService(sims4.service_manager.Service): TEST_WALKBY_SITUATION = sims4.tuning.tunable.TunableReference( description= '\n A walkby situation for testing.\n ', manager=services.get_instance_manager(sims4.resources.Types.SITUATION)) SOCIAL_AFFORDANCES = sims4.tuning.tunable.TunableList( description= '\n When selected for a walkby social the sim runs one of the social\n affordances in this list.\n ', tunable=SocialSuperInteraction.TunableReference()) SOCIAL_COOLDOWN = sims4.tuning.tunable.TunableSimMinute( description= '\n The minimum amount of time from the end of one social\n until the walkby sim can perform another social. If it is too small\n sims may socialize, stop, then start socializing again.\n ', default=60, minimum=30, maximum=480) SOCIAL_MAX_DURATION = sims4.tuning.tunable.TunableSimMinute( description= '\n The maximum amount of time the sims can socialize.\n ', default=60, minimum=1, maximum=180) SOCIAL_MAX_START_DISTANCE = sims4.tuning.geometric.TunableDistanceSquared( description= '\n Walkby Sims must be less than this distance apart for a social\n to be started.\n ', default=10) SOCIAL_VIEW_CONE_ANGLE = sims4.tuning.tunable.TunableAngle( description= '\n For 2 sims to be able to socialize at least one sim must be in the\n view cone of the other. This tunable defines the view cone as an angle\n in degrees centered straight out in front of the sim. 0 degrees would \n make the sim blind, 360 degrees means the sim can see in all directions.\n ', default=sims4.math.PI) SOCIAL_CHANCE_TO_START = sims4.tuning.tunable.TunablePercent( description= '\n This is the percentage chance, per pair of properly positioned sims,\n that a social will be started on an ambient service ping.\n\n The number of pairs of sims is multiplied by this tunable to get the overall\n chance of a social starting.\n \n For the purposes of these examples, we assume that the tuned value is 25%\n \n 1 pair of sims -> 25%.\n 2 pairs of sims -> 50%\n 4 pairs of sims -> 100%.\n\n ', default=100) def __init__(self): self._update_alarm_handle = None self._flavor_alarm_handle = None self._sources = [] def stop(self): if self._update_alarm_handle is not None: alarms.cancel_alarm(self._update_alarm_handle) self._update_alarm_handle = None if self._flavor_alarm_handle is not None: alarms.cancel_alarm(self._flavor_alarm_handle) self._flavor_alarm_handle = None @classproperty def save_error_code(cls): return persistence_error_types.ErrorCodes.SERVICE_SAVE_FAILED_AMBIENT_SERVICE def save(self, open_street_data=None, **kwargs): if open_street_data is None: return open_street_data.ambient_service = gameplay_serialization.AmbientServiceData( ) for source in self._sources: with ProtocolBufferRollback( open_street_data.ambient_service.sources) as source_data: source.save(source_data) def begin_walkbys(self): self._sources.append( _AmbientSourceStreet(_AmbientSource.DEFAULT_PRIORITY_MULTIPLIER)) self._sources.append( _AmbientSourceGhost(_AmbientSource.DEFAULT_PRIORITY_MULTIPLIER)) for source in self._sources: source.begin_scheduled_walkbys() open_street_id = services.current_zone().open_street_id open_street_data = services.get_persistence_service( ).get_open_street_proto_buff(open_street_id) if open_street_data is not None: for source_data in open_street_data.ambient_service.sources: for source in self._sources: if source.source_type == source_data.source_type: source.load(source_data) break self._update_alarm_handle = alarms.add_alarm( self, clock.interval_in_sim_minutes(5), self._update_alarm_callback, repeating=True, use_sleep_time=False) self._flavor_alarm_handle = alarms.add_alarm( self, clock.interval_in_sim_minutes(1), self._flavor_alarm_callback, repeating=True, use_sleep_time=False) def debug_update(self): return self._update(force_create=True) def start_specific_situation(self, situation_type): return self._sources[0].start_specific_situation(situation_type) def _update_alarm_callback(self, alarm_handle=None): client = services.client_manager().get_first_client() if client is None: return self._update() def _update(self, force_create=False): if not self._sources: return if gsi_handlers.ambient_handlers.archiver.enabled: gsi_description = self.get_gsi_description() else: gsi_description = None sources_and_priorities = [(source, source.get_priority()) for source in self._sources] sources_and_priorities.sort(key=lambda source: source[1], reverse=True) situation_id = None source = sources_and_priorities[0][0] priority = sources_and_priorities[0][1] if priority > 0: situation_id = source.start_appropriate_situation() elif force_create: for (source, _) in sources_and_priorities: situation_id = source.start_appropriate_situation() if situation_id is not None: break if gsi_handlers.ambient_handlers.archiver.enabled: situation = None if situation_id is not None: situation = services.current_zone().situation_manager.get( situation_id) gsi_handlers.ambient_handlers.archive_ambient_data( gsi_description, created_situation=str(situation)) return situation_id def _flavor_alarm_callback(self, _): if not self._sources: return social_available_sim_to_situation = {} flavor_available_sim_to_situation = {} for source in self._sources: for situation in source.get_running_situations(): if isinstance(situation, WalkbyAmbientSituation): sim = situation.get_sim_available_for_social() if sim is not None: social_available_sim_to_situation[sim] = situation sim = situation.get_sim_available_for_walkby_flavor() if sim is not None: flavor_available_sim_to_situation[sim] = situation social_available_sims = list(social_available_sim_to_situation.keys()) available_social_pairs = [] for (actor_sim, target_sim) in itertools.combinations(social_available_sims, 2): if self._can_sims_start_social(actor_sim, target_sim): available_social_pairs.append((actor_sim, target_sim)) if available_social_pairs and sims4.random.random_chance( len(available_social_pairs) * self.SOCIAL_CHANCE_TO_START * 100): (actor_sim, target_sim) = available_social_pairs[random.randint( 0, len(available_social_pairs) - 1)] social_available_sim_to_situation[actor_sim].start_social( social_available_sim_to_situation[target_sim]) flavor_available_sim_to_situation.pop(actor_sim, None) flavor_available_sim_to_situation.pop(target_sim, None) for situation in flavor_available_sim_to_situation.values(): if situation.random_chance_to_start_flavor_interaction(): situation.start_flavor_interaction() break def _sim_forward_to_sim_dot(self, sim_one, sim_two): one_to_two = sim_two.position - sim_one.position one_to_two.y = 0 if sims4.math.vector3_almost_equal(one_to_two, sims4.math.Vector3.ZERO()): return 1 one_to_two = sims4.math.vector_normalize(one_to_two) one_to_two_dot = sims4.math.vector_dot_2d( sims4.math.vector_flatten(sim_one.forward), one_to_two) return one_to_two_dot def _can_sims_start_social(self, actor_sim, target_sim): distance_squared = (actor_sim.position - target_sim.position).magnitude_squared() if distance_squared > self.SOCIAL_MAX_START_DISTANCE: return False cone_dot = math.cos(self.SOCIAL_VIEW_CONE_ANGLE * 0.5) actor_to_target_dot = self._sim_forward_to_sim_dot( actor_sim, target_sim) if actor_to_target_dot <= cone_dot: target_to_actor_dot = self._sim_forward_to_sim_dot( target_sim, actor_sim) if target_to_actor_dot <= cone_dot: return False if terrain.is_position_in_street(actor_sim.position): return False if terrain.is_position_in_street(target_sim.position): return False else: middle_position = (actor_sim.position + target_sim.position) * 0.5 if terrain.is_position_in_street(middle_position): return False return True def get_gsi_description(self): if not self._sources: return '' description = self._sources[0].get_gsi_description() for source in self._sources[1:]: description = description + ' ' + source.get_gsi_description() return description
def _tunable_tests_enabled(): return SocialSuperInteraction._tunable_tests_enabled()
class _ClubPickerActionChallenge(HasTunableSingletonFactory, AutoFactoryInit): FACTORY_TUNABLES = { 'challenge_game': GameRules.TunableReference( description= '\n The game that the club is being challenged at. This is used to\n determine how many Sims are required, per team.\n ' ), 'challenge_social_interaction': SocialSuperInteraction.TunableReference( description= '\n Specify an interaction that the challenging Sim runs on a Sim in\n the challenged club (usually the leader). Once this interaction\n completes, the challenge executes.\n ' ), 'challenge_interaction': SuperInteraction.TunableReference( description= '\n The interaction to push on the Sims being challenged.\n ' ) } def on_choice_selected(self, interaction, picked_items, **kwargs): club_service = services.get_club_service() if club_service is None: return actor_club_gathering = club_service.sims_to_gatherings_map.get( interaction.sim) if actor_club_gathering is None: return def _on_challenge_social_interaction_finished( challenge_social_interaction): if not challenge_social_interaction.is_finishing_naturally: return minimum_players_per_team = math.ceil( self.challenge_game.players_per_game.lower_bound / self.challenge_game.teams_per_game.lower_bound) maximum_players_per_team = math.floor( self.challenge_game.players_per_game.upper_bound / self.challenge_game.teams_per_game.upper_bound) teams = [] for (_, club) in zip( range(self.challenge_game.teams_per_game.upper_bound), itertools.chain( (actor_club_gathering.associated_club, ), picked_items)): club_gathering = club_service.clubs_to_gatherings_map.get( club) if club_gathering is None: continue club_team = set() challenger_sims = ( interaction.sim, ) if actor_club_gathering.associated_club is club else () for club_member in itertools.chain( challenger_sims, sorted(club_gathering.all_sims_in_situation_gen(), key=lambda sim: sim.sim_info is not club. leader)): club_team.add(club_member) if len(club_team) >= maximum_players_per_team: break if len(club_team) >= minimum_players_per_team: teams.append(club_team) if len(teams) < self.challenge_game.teams_per_game.lower_bound: return all_sims = tuple(itertools.chain.from_iterable(teams)) game_transition_destination_node_validator = GameTransitionDestinationNodeValidator( self.challenge_game, teams=teams) for sim in all_sims: context = challenge_social_interaction.context.clone_for_sim( sim, group_id=challenge_social_interaction.group_id, source_interaction_id=challenge_social_interaction.id, source_interaction_sim_id=challenge_social_interaction. sim.sim_id, insert_strategy=QueueInsertStrategy.NEXT) sim.push_super_affordance( self.challenge_interaction, interaction.target, context, game_transition_destination_node_validator= game_transition_destination_node_validator) for club in picked_items: club_gathering = club_service.clubs_to_gatherings_map.get(club) if club_gathering is None: continue for club_member in sorted( club_gathering.all_sims_in_situation_gen(), key=lambda sim: sim.sim_info is not club.leader): context = interaction.context.clone_from_immediate_context( interaction) execute_result = interaction.sim.push_super_affordance( self.challenge_social_interaction, club_member, context) if execute_result: challenge_social_interaction = execute_result.interaction challenge_social_interaction.register_on_finishing_callback( _on_challenge_social_interaction_finished) break