示例#1
0
 def _select_sims_from_results(self, results, sims_to_spawn):
     self._selected_sim_infos = []
     global_blacklist = services.sim_filter_service().get_global_blacklist()
     for result in tuple(results):
         while result.sim_info.is_instanced(allow_hidden_flags=ALL_HIDDEN_REASONS) or result.sim_info.id in self._blacklist_sim_ids or result.sim_info.id in global_blacklist:
             results.remove(result)
     sorted_results = sorted(results, key=lambda x: x.score, reverse=True)
     if self._sim_filter.use_weighted_random:
         index = filters.tunable.TunableSimFilter.TOP_NUMBER_OF_SIMS_TO_LOOK_AT
         randomization_group = [(result.score, result.sim_info) for result in sorted_results[:index]]
         while index < len(sorted_results):
             while len(self._selected_sim_infos) < sims_to_spawn:
                 random_choice = random.pop_weighted(randomization_group)
                 randomization_group.append((sorted_results[index].score, sorted_results[index].sim_info))
                 logger.info('Sim ID matching request {0}', random_choice)
                 self._selected_sim_infos.append(random_choice)
                 index += 1
         while True:
             while randomization_group and len(self._selected_sim_infos) < self._number_of_sims_to_find:
                 random_choice = random.pop_weighted(randomization_group)
                 logger.info('Sim ID matching request {0}', random_choice)
                 self._selected_sim_infos.append(random_choice)
     else:
         for result in sorted_results:
             if len(self._selected_sim_infos) == sims_to_spawn:
                 break
             logger.info('Sim ID matching request {0}', result.sim_info)
             self._selected_sim_infos.append(result.sim_info)
     return self._selected_sim_infos
示例#2
0
 def _select_sims_from_results(self, results, sims_to_spawn):
     self._filter_results = []
     self._filter_results_info = []
     global_blacklist = services.sim_filter_service().get_global_blacklist()
     for result in tuple(results):
         if not result.sim_info.is_instanced(
                 allow_hidden_flags=ALL_HIDDEN_REASONS):
             if not result.sim_info.id in self._blacklist_sim_ids:
                 if result.sim_info.id in global_blacklist:
                     results.remove(result)
         results.remove(result)
     sorted_results = sorted(results, key=lambda x: x.score, reverse=True)
     if self._sim_filter.use_weighted_random:
         index = filters.tunable.TunableSimFilter.TOP_NUMBER_OF_SIMS_TO_LOOK_AT
         randomization_group = [(result.score, result)
                                for result in sorted_results[:index]]
         while index < len(sorted_results):
             while len(self._filter_results) < sims_to_spawn:
                 random_choice = random.pop_weighted(randomization_group)
                 if index < len(sorted_results):
                     randomization_group.append(
                         (sorted_results[index].score,
                          sorted_results[index]))
                     index += 1
                 logger.info('Sim ID matching request {0}', random_choice)
                 self._filter_results.append(random_choice)
                 self._filter_results_info.append(random_choice.sim_info)
                 index += 1
         if len(self._filter_results) < sims_to_spawn:
             if randomization_group:
                 while True:
                     while randomization_group and len(
                             self._filter_results
                     ) < self._number_of_sims_to_find:
                         random_choice = random.pop_weighted(
                             randomization_group)
                         logger.info('Sim ID matching request {0}',
                                     random_choice)
                         self._filter_results.append(random_choice)
                         self._filter_results_info.append(
                             random_choice.sim_info)
     else:
         for result in sorted_results:
             if len(self._filter_results) == sims_to_spawn:
                 break
             logger.info('Sim ID matching request {0}', result.sim_info)
             self._filter_results.append(result)
             self._filter_results_info.append(result.sim_info)
     if self._sim_gsi_logging_data is not None:
         for result in self._filter_results:
             sim_filter_handlers.archive_filter_request(
                 result.sim_info,
                 self._sim_gsi_logging_data,
                 rejected=False,
                 reason='Score > 0 and chosen for spawning')
     return self._filter_results
示例#3
0
 def on_state_activated(self, reader=None, preroll_time_override=None):
     super().on_state_activated(reader=reader,
                                preroll_time_override=preroll_time_override)
     if reader is not None:
         self._contest_situation_ids = reader.read_uint64s(
             CONTEST_TOKENS, None)
     if self._contest_situation_ids is None:
         self._contest_situation_ids = []
         situation_manager = services.get_zone_situation_manager()
         max_contests = self._number_of_contests.upper_bound
         min_contests = self._number_of_contests.lower_bound
         if max_contests is None:
             max_contests = len(self._possible_contests)
         else:
             max_contests = min(len(self._possible_contests), max_contests)
         if min_contests is None:
             min_contests = len(self._possible_contests)
         min_contests = min(min_contests, max_contests)
         contest_count = random.random.randint(min_contests, max_contests)
         possible_situations = [
             (possible_contest.weight, possible_contest.situation)
             for possible_contest in self._possible_contests
         ]
         while contest_count:
             contest_count -= 1
             chosen_situation = random.pop_weighted(possible_situations)
             guest_list = SituationGuestList(invite_only=True)
             self._contest_situation_ids.append(
                 situation_manager.create_situation(chosen_situation,
                                                    guest_list=guest_list,
                                                    user_facing=False))
示例#4
0
 def _trigger_phone_call_gen(self, timeline):
     client = services.client_manager().get_first_client()
     if client is None:
         return
     client_household = client.household
     if client_household is None:
         return
     sims_to_check = [sim for sim in client_household.instanced_sims_gen()]
     random.shuffle(sims_to_check)
     for sim in sims_to_check:
         call_types = []
         ask_to_come_over_phone_call = HouseholdManager.PHONE_CALL_INFO.ask_to_come_over(sim)
         call_types.append((ask_to_come_over_phone_call.weight, ask_to_come_over_phone_call))
         chat_phone_call = HouseholdManager.PHONE_CALL_INFO.chat(sim)
         call_types.append((chat_phone_call.weight, chat_phone_call))
         invite_over_phone_call = HouseholdManager.PHONE_CALL_INFO.invite_over(sim)
         call_types.append((invite_over_phone_call.weight, invite_over_phone_call))
         while call_types:
             call_type = pop_weighted(call_types)
             if call_type.try_and_setup():
                 call_type.execute()
                 self._phone_call_element = None
                 return
             yield element_utils.run_child(timeline, element_utils.sleep_until_next_tick_element())
     self._phone_call_element = None
 def create_offspring_data(self):
     r = random.Random()
     r.seed(self._seed)
     offspring_count = pop_weighted(
         [(p.weight *
           p.modifiers.get_multiplier(SingleSimResolver(self._sim_info)),
           p.size) for p in self.MULTIPLE_OFFSPRING_CHANCES],
         random=r)
     offspring_count = min(self._sim_info.household.free_slot_count + 1,
                           offspring_count)
     self._offspring_data = []
     for offspring_index in range(offspring_count):
         if offspring_index and r.random(
         ) < self.MONOZYGOTIC_OFFSPRING_CHANCE:
             gender = self._offspring_data[offspring_index - 1].gender
             genetics = self._offspring_data[offspring_index - 1].genetics
         else:
             gender = Gender.MALE if r.random() < 0.5 else Gender.FEMALE
             genetics = r.randint(1, MAX_UINT32)
         last_name = SimSpawner.get_family_name_for_gender(
             self._sim_info.account, self._sim_info.last_name,
             gender == Gender.FEMALE)
         traits = self._select_traits_for_offspring(gender)
         self._offspring_data.append(
             PregnancyOffspringData(gender,
                                    genetics,
                                    last_name=last_name,
                                    traits=traits))
示例#6
0
 def score_and_schedule_nodes_gen(self, nodes_to_score, nodes_to_schedule, specific_time=None, time_modifier=TimeSpan.ZERO, timeline=None, gsi_data=None, **additional_drama_node_kwargs):
     active_household = services.active_household()
     if active_household is None:
         return
     self._update_cooldowns()
     sim_resolvers = tuple(SingleSimResolver(sim_info) for sim_info in active_household.sim_info_gen())
     possible_nodes = []
     chosen_node_types = set()
     for drama_node in nodes_to_score:
         if not self._is_node_on_cooldown(drama_node) or gsi_data is not None:
             gsi_data.rejected_nodes.append(GSIRejectedDramaNodeScoringData(drama_node, '{} is on cooldown.', drama_node))
             for resolver in sim_resolvers:
                 uid = id_generator.generate_object_id()
                 drama_node_inst = drama_node(uid)
                 result = drama_node_inst.setup(resolver, gsi_data=gsi_data, **additional_drama_node_kwargs)
                 if timeline is not None:
                     yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0)))
                 if not result:
                     drama_node_inst.cleanup()
                 else:
                     score = drama_node_inst.score()
                     if score == 0:
                         if gsi_data is not None:
                             gsi_data.rejected_nodes.append(GSIRejectedDramaNodeScoringData(drama_node, 'Scoring generated a score of 0.', score=score, receiver=drama_node_inst.get_receiver_sim_info(), sender=drama_node_inst.get_sender_sim_info()))
                         drama_node_inst.cleanup()
                     else:
                         if gsi_data is not None:
                             gsi_data.potential_nodes.append(GSIDramaNodeScoringData(drama_node, score, drama_node_inst.get_score_details(), drama_node_inst.get_receiver_sim_info(), drama_node_inst.get_sender_sim_info()))
                         possible_nodes.append((score, drama_node_inst))
     if not possible_nodes:
         return
     while nodes_to_schedule > 0:
         while possible_nodes:
             chosen_node = random.pop_weighted(possible_nodes)
             if type(chosen_node) in chosen_node_types:
                 if gsi_data is not None:
                     gsi_data.rejected_nodes.append(GSIRejectedDramaNodeScoringData(type(drama_node_inst), 'Could not schedule drama node because a drama node of this type was already scheduled.', score=chosen_node.score(), score_details=chosen_node.get_score_details(), receiver=chosen_node.get_receiver_sim_info(), sender=chosen_node.get_sender_sim_info()))
                 chosen_node.cleanup()
             else:
                 result = chosen_node.schedule(None, specific_time=specific_time, time_modifier=time_modifier)
                 if timeline is not None:
                     yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0)))
                 if not result:
                     if gsi_data is not None:
                         gsi_data.rejected_nodes.append(GSIRejectedDramaNodeScoringData(type(chosen_node), 'Could not schedule drama node because there are no valid times.', score=chosen_node.score(), score_details=chosen_node.get_score_details(), receiver=chosen_node.get_receiver_sim_info(), sender=chosen_node.get_sender_sim_info()))
                     chosen_node.cleanup()
                 else:
                     if gsi_data is not None:
                         gsi_data.chosen_nodes.append(GSIDramaNodeScoringData(type(chosen_node), chosen_node.score(), chosen_node.get_score_details(), chosen_node.get_receiver_sim_info(), chosen_node.get_sender_sim_info()))
                     self._scheduled_nodes[chosen_node.uid] = chosen_node
                     if chosen_node.cooldown is not None and chosen_node.cooldown.cooldown_option == CooldownOption.ON_SCHEDULE:
                         self.start_cooldown(type(chosen_node))
                     if is_drama_node_log_enabled():
                         log_drama_node_scoring(chosen_node, DramaNodeLogActions.SCHEDULED)
                     nodes_to_schedule -= 1
                     chosen_node_types.add(type(chosen_node))
     for (score, drama_node_inst) in possible_nodes:
         drama_node_inst.cleanup()
 def get_jog_waypoint_constraints(cls, context):
     sim = context.sim
     if context.pick is not None:
         pick_position = context.pick.location
         pick_vector = pick_position - sim.position
         pick_vector /= pick_vector.magnitude()
     else:
         pick_vector = sim.forward
     zone = services.current_zone()
     active_lot = zone.lot
     lot_corners = active_lot.corners
     sim_poly = sims4.geometry.CompoundPolygon(sims4.geometry.Polygon([sim.position]))
     lot_poly = sims4.geometry.CompoundPolygon(sims4.geometry.Polygon([corner for corner in lot_corners]))
     intersection = lot_poly.intersect(sim_poly)
     sim_on_lot = len(intersection) >= 1
     if sim_on_lot:
         spawn_point = zone.get_spawn_point(lot_id=active_lot.lot_id, sim_spawner_tags=SimInfoSpawnerTags.SIM_SPAWNER_TAGS)
         origin_position = spawn_point.center
         routing_surface = routing.SurfaceIdentifier(zone.id, 0, routing.SURFACETYPE_WORLD)
         except_lot_id = active_lot.lot_id
     else:
         origin_position = sim.position
         routing_surface = sim.routing_surface
         except_lot_id = None
     interaction_constraint = Circle(origin_position, cls.CONSTRAINT_RADIUS, routing_surface=routing_surface, los_reference_point=None)
     jog_waypoint_constraints = []
     zone = services.current_zone()
     active_lot = zone.lot
     constraint_set = zone.get_spawn_points_constraint(except_lot_id=except_lot_id)
     constraints_weighted = []
     min_score = sims4.math.MAX_FLOAT
     for constraint in constraint_set:
         spawn_point_vector = constraint.average_position - sim.position
         score = sims4.math.vector_dot_2d(pick_vector, spawn_point_vector)
         if score < min_score:
             min_score = score
         constraints_weighted.append((score, constraint))
     constraints_weighted = [(score - min_score, constraint) for (score, constraint) in constraints_weighted]
     constraints_weighted = sorted(constraints_weighted, key=lambda i: i[0])
     first_constraint = constraints_weighted[-1][1]
     del constraints_weighted[-1]
     first_constraint_circle = Circle(first_constraint.average_position, cls.CONSTRAINT_RADIUS, routing_surface=first_constraint.routing_surface)
     jog_waypoint_constraints.append(first_constraint_circle)
     last_waypoint_position = first_constraint.average_position
     for _ in range(cls.NUM_JOG_POINTS - 1):
         constraints_weighted_next = []
         for (_, constraint) in constraints_weighted:
             average_position = constraint.average_position
             distance_last = (average_position - last_waypoint_position).magnitude_2d()
             distance_home = (average_position - origin_position).magnitude_2d()
             constraints_weighted_next.append((distance_last + distance_home, constraint))
         next_constraint = pop_weighted(constraints_weighted_next)
         next_constraint_circle = Circle(next_constraint.average_position, cls.CONSTRAINT_RADIUS, routing_surface=next_constraint.routing_surface)
         jog_waypoint_constraints.append(next_constraint_circle)
         constraints_weighted = constraints_weighted_next
         last_waypoint_position = next_constraint.average_position
     jog_waypoint_constraints.append(interaction_constraint)
     return (interaction_constraint, jog_waypoint_constraints)
示例#8
0
 def try_place_object(self, obj, resolver, **kwargs):
     for strategy_group in self.placement_strategy_groups:
         strategies = [(entry.weight.get_multiplier(resolver),
                        entry.placement_strategy)
                       for entry in strategy_group]
         while strategies:
             strategy = pop_weighted(strategies)
             if strategy.try_place_object(obj, resolver, **kwargs):
                 return True
     return False
示例#9
0
    def get_winners(self, contest):
        num_rewards = len(contest.festival_contest_tuning._win_rewards)
        scores = [(contest_score.score, contest_score)
                  for contest_score in contest._scores]
        winners = []
        while scores:
            if len(winners) < num_rewards:
                winner = random.pop_weighted(scores)
                winners.append(winner)

        return winners
示例#10
0
 def create_offspring_data(self):
     r = random.Random()
     r.seed(self._seed)
     if self._offspring_count_override is not None:
         offspring_count = self._offspring_count_override
     else:
         offspring_count = pop_weighted([
             (p.weight *
              p.modifiers.get_multiplier(SingleSimResolver(self._sim_info)),
              p.size) for p in self.MULTIPLE_OFFSPRING_CHANCES
         ],
                                        random=r)
     offspring_count = min(self._sim_info.household.free_slot_count + 1,
                           offspring_count)
     species = self._sim_info.species
     age = self._sim_info.get_birth_age()
     aging_data = AgingTuning.get_aging_data(species)
     num_personality_traits = aging_data.get_personality_trait_count(age)
     self._offspring_data = []
     for offspring_index in range(offspring_count):
         if offspring_index and r.random(
         ) < self.MONOZYGOTIC_OFFSPRING_CHANCE:
             gender = self._offspring_data[offspring_index - 1].gender
             genetics = self._offspring_data[offspring_index - 1].genetics
         else:
             gender_chance_stat = self._sim_info.get_statistic(
                 self.GENDER_CHANCE_STAT)
             if gender_chance_stat is None:
                 gender_chance = 0.5
             else:
                 gender_chance = (gender_chance_stat.get_value() -
                                  gender_chance_stat.min_value) / (
                                      gender_chance_stat.max_value -
                                      gender_chance_stat.min_value)
             gender = Gender.FEMALE if r.random(
             ) < gender_chance else Gender.MALE
             genetics = r.randint(1, MAX_UINT32)
         last_name = SimSpawner.get_last_name(self._sim_info.last_name,
                                              gender, species)
         offspring_data = PregnancyOffspringData(age,
                                                 gender,
                                                 species,
                                                 genetics,
                                                 last_name=last_name)
         (parent_a, parent_b) = self.get_parents()
         offspring_data.traits = self.select_traits_for_offspring(
             offspring_data,
             parent_a,
             parent_b,
             num_personality_traits,
             origin=self._origin)
         self._offspring_data.append(offspring_data)
示例#11
0
 def add_quirks(self, sim_info):
     trait_tracker = sim_info.trait_tracker
     r = random.Random(sim_info.sim_id)
     for quirk_set in self._quirk_sets:
         quirk_count = quirk_set.count(sim_info, r)
         quirk_count_current = sum(1 for entry in quirk_set.entries if sim_info.has_trait(entry.quirk_trait))
         allowed_entries = [(entry.quirk_relative_weight, entry.quirk_trait) for entry in quirk_set.entries if trait_tracker.can_add_trait(entry.quirk_trait)]
         while allowed_entries:
             if quirk_count_current >= quirk_count:
                 break
             quirk_trait = pop_weighted(allowed_entries, random=r)
             if sim_info.add_trait(quirk_trait):
                 quirk_count_current += 1
示例#12
0
 def create_offspring_data(self):
     r = random.Random()
     r.seed(self._seed)
     offspring_count = pop_weighted([(p.weight*p.modifiers.get_multiplier(SingleSimResolver(self._sim_info)), p.size) for p in self.MULTIPLE_OFFSPRING_CHANCES], random=r)
     offspring_count = min(self._sim_info.household.free_slot_count + 1, offspring_count)
     self._offspring_data = []
     for offspring_index in range(offspring_count):
         if offspring_index and r.random() < self.MONOZYGOTIC_OFFSPRING_CHANCE:
             gender = self._offspring_data[offspring_index - 1].gender
             genetics = self._offspring_data[offspring_index - 1].genetics
         else:
             gender = Gender.MALE if r.random() < 0.5 else Gender.FEMALE
             genetics = r.randint(1, MAX_UINT32)
         last_name = SimSpawner.get_family_name_for_gender(self._sim_info.account, self._sim_info.last_name, gender == Gender.FEMALE)
         traits = self._select_traits_for_offspring(gender)
         self._offspring_data.append(PregnancyOffspringData(gender, genetics, last_name=last_name, traits=traits))
 def get_waypoint_constraints_gen(self, routing_agent, waypoint_count):
     zone = services.current_zone()
     constraint_set = zone.get_spawn_points_constraint(except_lot_id=self._except_lot_id, sim_spawner_tags=self.spawn_point_tags, generalize=True)
     routing_context = routing_agent.routing_component.pathplan_context
     source_handle = routing.connectivity.Handle(routing_agent.position, routing_agent.routing_surface)
     dest_handles = set()
     for constraint in constraint_set:
         handles = constraint.get_connectivity_handles(routing_agent)
         dest_handles.update(handles)
     connectivity = routing.test_connectivity_batch((source_handle,), dest_handles, routing_context=routing_context, compute_cost=True)
     vehicle_dest_handles = {dest for (_, dest, cost) in connectivity if sims4.math.almost_equal(cost, 0.0)}
     constraint_set = create_constraint_set([handle.constraint for handle in vehicle_dest_handles])
     constraints_weighted = []
     min_score = sims4.math.MAX_FLOAT
     for constraint in constraint_set:
         spawn_point_vector = constraint.average_position - self._sim.position
         score = sims4.math.vector_dot_2d(self._pick_vector, spawn_point_vector)
         min_score = score
         constraints_weighted.append((score, constraint))
     constraints_weighted = [(score - min_score, constraint) for (score, constraint) in constraints_weighted]
     constraints_weighted = sorted(constraints_weighted, key=lambda i: i[0])
     first_constraint = constraints_weighted[-1][1]
     del constraints_weighted[-1]
     first_constraint_circle = Circle(first_constraint.average_position, self.constraint_radius, routing_surface=first_constraint.routing_surface)
     jog_waypoint_constraints = []
     jog_waypoint_constraints.append(first_constraint_circle)
     last_waypoint_position = first_constraint.average_position
     for _ in range(waypoint_count - 1):
         constraints_weighted_next = []
         for (_, constraint) in constraints_weighted:
             average_position = constraint.average_position
             distance_last = (average_position - last_waypoint_position).magnitude_2d()
             distance_home = (average_position - self._origin_position).magnitude_2d()
             constraints_weighted_next.append((distance_last + distance_home, constraint))
         break
         next_constraint = pop_weighted(constraints_weighted_next)
         next_constraint_circle = Circle(next_constraint.average_position, self.constraint_radius, routing_surface=next_constraint.routing_surface)
         jog_waypoint_constraints.append(next_constraint_circle)
         constraints_weighted = constraints_weighted_next
         break
         last_waypoint_position = next_constraint.average_position
     jog_waypoint_constraints = self.apply_water_constraint(jog_waypoint_constraints)
     yield from jog_waypoint_constraints
     yield self._start_constraint
示例#14
0
    def select_traits_for_offspring(cls,
                                    offspring_data,
                                    parent_a,
                                    parent_b,
                                    num_traits,
                                    origin=PregnancyOrigin.DEFAULT,
                                    random=random):
        traits = []
        personality_trait_slots = num_traits

        def _add_trait_if_possible(selected_trait):
            nonlocal personality_trait_slots
            if selected_trait in traits:
                return False
            if any(t.is_conflicting(selected_trait) for t in traits):
                return False
            if selected_trait.is_personality_trait:
                if not personality_trait_slots:
                    return False
                personality_trait_slots -= 1
            traits.append(selected_trait)
            return True

        if origin in cls.PREGNANCY_ORIGIN_MODIFIERS:
            trait_entries = cls.PREGNANCY_ORIGIN_MODIFIERS[
                origin].trait_entries
            for trait_entry in trait_entries:
                if random.random() >= trait_entry.chance:
                    continue
                selected_trait = pop_weighted(
                    [(t.weight, t.trait) for t in trait_entry.traits
                     if t.trait.is_valid_trait(offspring_data)],
                    random=random)
                if selected_trait is not None:
                    _add_trait_if_possible(selected_trait)
        if parent_a is not None:
            if parent_b is not None:
                for inherited_trait_entries in parent_a.trait_tracker.get_inherited_traits(
                        parent_b):
                    selected_trait = pop_weighted(
                        list(inherited_trait_entries), random=random)
                    if selected_trait is not None:
                        _add_trait_if_possible(selected_trait)
        if not personality_trait_slots:
            return traits
        personality_traits = get_possible_traits(offspring_data)
        random.shuffle(personality_traits)
        while True:
            current_trait = personality_traits.pop()
            if _add_trait_if_possible(current_trait):
                break
            if not personality_traits:
                return traits
        if not personality_trait_slots:
            return traits
        traits_a = set(parent_a.trait_tracker.personality_traits)
        traits_b = set(parent_b.trait_tracker.personality_traits)
        shared_parent_traits = list(
            traits_a.intersection(traits_b) - set(traits))
        random.shuffle(shared_parent_traits)
        while personality_trait_slots:
            while shared_parent_traits:
                current_trait = shared_parent_traits.pop()
                if current_trait in personality_traits:
                    personality_traits.remove(current_trait)
                did_add_trait = _add_trait_if_possible(current_trait)
                if did_add_trait:
                    if not personality_trait_slots:
                        return traits
        remaining_parent_traits = list(
            traits_a.symmetric_difference(traits_b) - set(traits))
        random.shuffle(remaining_parent_traits)
        while personality_trait_slots:
            while remaining_parent_traits:
                current_trait = remaining_parent_traits.pop()
                if current_trait in personality_traits:
                    personality_traits.remove(current_trait)
                did_add_trait = _add_trait_if_possible(current_trait)
                if did_add_trait:
                    if not personality_trait_slots:
                        return traits
        while personality_trait_slots:
            while personality_traits:
                current_trait = personality_traits.pop()
                _add_trait_if_possible(current_trait)
        return traits