Exemplo n.º 1
0
 def load_etree_node(self, node, source, expect_error):
     value = super().load_etree_node(node, source, expect_error)
     modified_dict = {}
     for (weather_type, loots) in value.items():
         if not loots.start_loot:
             if loots.end_loot:
                 modified_dict[weather_type] = loots
         modified_dict[weather_type] = loots
     return frozendict(modified_dict)
Exemplo n.º 2
0
 def _get_ancestor_family_tree(self):
     if self._ancestor_depths is None:
         ancestor_depths = {}
         ancestor_depths[self._owner_id] = 0
         for (relation, sim_id) in self._family_relations.items():
             relation_depth = 1 if relation <= FamilyRelationshipIndex.FATHER else 2
             ancestor_depths[sim_id] = relation_depth
         self._ancestor_depths = frozendict(ancestor_depths)
     return self._ancestor_depths
Exemplo n.º 3
0
	def multiplier_options (use_tooltip = False):
		tuple_elements = { }
		if use_tooltip:
			tuple_elements['tooltip'] = OptionalTunable(description = '\n                                            If enabled, provides a tooltip for\n                                            this entry if it is the first entry\n                                            to pass its tests.\n                                            \n                                            Future: Offer ways to combine tooltips in separated lists, etc.\n                                            ', tunable = TunableLocalizedStringFactory())
		else:
			tuple_elements['locked_args'] = {
				'tooltip': frozendict()
			}
		return {
			'multipliers': _get_tunable_multiplier_list_entry(**tuple_elements)
		}
Exemplo n.º 4
0
 def __init__(self,
              *args,
              dialog=None,
              on_response=None,
              additional_tokens=(),
              dialog_kwargs=frozendict(),
              **kwargs):
     super().__init__(**kwargs)
     merged_kwargs = dialog_kwargs.copy()
     merged_kwargs.update(kwargs)
     self._dialog = dialog(*args, **merged_kwargs)
     self._dialog.add_listener(self._on_response)
     self._result = None
     self._on_response = on_response
     self._additional_tokens = additional_tokens
Exemplo n.º 5
0
class _SingleJobComplexSituationState(CommonSituationState):
    FACTORY_TUNABLES = {
        'role_state':
        RoleState.TunableReference(
            description=
            '\n            The role the Sim has while in this state.\n            \n            This is the initial state.\n            '
        ),
        'locked_args': {
            'job_and_role_changes': frozendict()
        }
    }

    def __init__(self, *args, situation_job, role_state, job_and_role_changes,
                 **kwargs):
        super().__init__(*args,
                         job_and_role_changes={situation_job: role_state},
                         **kwargs)
Exemplo n.º 6
0
class _DoStuffState(CommonSituationState):
    FACTORY_TUNABLES = {
        'job_and_role_gym_stuff':
        TunableMapping(
            description=
            '\n                A mapping between situation jobs and role states that defines\n                what role states we want to switch to for sims on which jobs\n                when this situation state is entered.\n                ',
            key_type=TunableReference(
                description=
                "\n                    A reference to a SituationJob that we will use to change\n                    sim's role state.\n                    ",
                manager=services.situation_job_manager(),
                pack_safe=True),
            key_name='Situation Job',
            value_type=TunableReference(
                description=
                '\n                    The role state that we will switch sims of the linked job\n                    into.\n                    ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ROLE_STATE),
                pack_safe=True),
            value_name='Role State'),
        'do_stuff_timeout':
        OptionalTunable(
            description=
            '\n                Optional tunable for when to end the Do Stuff state. \n    \n                If this is enabled then the Do Stuff state will eventually time\n                out and end the situation.\n                \n                If this is disabled the situation will just stay in the Do Stuff\n                state forever.\n                ',
            tunable=TunableTuple(
                description='\n                \n                    ',
                min_time=TunableSimMinute(
                    description=
                    '\n                        The length of time to wait before advancing to the\n                        Change Clothes state.\n                        ',
                    default=60),
                max_time=TunableSimMinute(
                    description=
                    '\n                        The maximum time a visitor will spend on the relaxation\n                        venue as a guest.\n                        ',
                    default=60))),
        'locked_args': {
            'job_and_role_changes': frozendict()
        }
    }

    def __init__(self, job_and_role_gym_stuff, do_stuff_timeout, **kwargs):
        super().__init__(**kwargs)
        self._job_and_role_gym_stuff = job_and_role_gym_stuff
        self._do_stuff_timeout = do_stuff_timeout

    def on_activate(self, reader=None):
        super().on_activate(reader)
        if self._do_stuff_timeout is not None:
            duration = random.uniform(self._do_stuff_timeout.min_time,
                                      self._do_stuff_timeout.max_time)
            self._create_or_load_alarm(DO_STUFF_TIMEOUT,
                                       duration,
                                       lambda _: self.timer_expired(),
                                       should_persist=True,
                                       reader=reader)

    def _set_job_role_state(self):
        for (job, role_state) in self._job_and_role_gym_stuff.items():
            if job is not None:
                if role_state is not None:
                    self.owner._set_job_role_state(job, role_state)

    def _get_remaining_time_for_gsi(self):
        return self._get_remaining_alarm_time(DO_STUFF_TIMEOUT)

    def timer_expired(self):
        self.owner._self_destruct()
Exemplo n.º 7
0
class _PreparationState(CommonSituationState):
    FACTORY_TUNABLES = {'creation_ops': TunableList(tunable=ObjectCreationOp.TunableFactory(description='\n                The operation that will create the objects.\n                ', locked_args={'destroy_on_placement_failure': True})), 'locked_args': {'job_and_role_changes': frozendict(), 'allow_join_situation': False, 'time_out': None}}

    def __init__(self, *args, creation_ops=(), **kwargs):
        super().__init__(*args, **kwargs)
        self.creation_ops = creation_ops

    def on_activate(self, reader=None):
        super().on_activate(reader=reader)
        resolver = self.owner._get_placement_resolver()
        for operation in self.creation_ops:
            operation.apply_to_resolver(resolver)
        self.owner.on_objects_ready()
Exemplo n.º 8
0
class _WaitingToMoveState(CommonSituationState):
    FACTORY_TUNABLES = {'locked_args': {'job_and_role_changes': frozendict(), 'allow_join_situation': False}}

    def timer_expired(self):
        self.owner.on_ready_to_move()
Exemplo n.º 9
0
class GameObject(ClientObjectMixin, ReservationMixin, ScriptObject, reset.ResettableObjectMixin):
    INSTANCE_TUNABLES = {'_transient_tuning': Tunable(description='\n            If transient the object will always be destroyed and never put down.\n            ', tunable_type=bool, default=False, tuning_filter=FilterTag.EXPERT_MODE, display_name='Transient'), 'additional_interaction_constraints': TunableList(description='\n            A list of constraints that must be fulfilled in order to run the \n            linked affordances. This should only be used when the same \n            affordance uses different constraints based on the object.\n            ', tunable=TunableTuple(constraint=TunableConstraintVariant(description='\n                    A constraint that must be fulfilled in order to interact with this object.\n                    '), affordance_links=TunableAffordanceFilterSnippet()), tuning_filter=FilterTag.EXPERT_MODE), 'autonomy_modifiers': TunableList(description='\n            List of autonomy modifiers that will be applied to the tuned\n            participant type.  These can be used to tune object variations.\n            ', tunable=TunableAutonomyModifier(locked_args={'commodities_to_add': (), 'score_multipliers': frozendict(), 'provided_affordance_compatibility': None, 'super_affordance_suppression_mode': autonomy.autonomy_modifier.SuperAffordanceSuppression.AUTONOMOUS_ONLY, 'suppress_self_affordances': False, 'only_scored_static_commodities': None, 'only_scored_stats': None, 'relationship_multipliers': None})), 'set_ico_as_carry_target': Tunable(description="\n            Whether or not the crafting process should set the carry target\n            to be the ICO.  Example Usage: Sheet Music has this set to false\n            because the sheet music is in the Sim's inventory and the Sim needs\n            to carry the guitar/violin.  This is a tunable on game object\n            because the ICO in the crafting process can be any game object.\n            ", tunable_type=bool, default=True), 'supported_posture_types': TunablePostureTypeListSnippet(description='\n            The postures supported by this part. If empty, assumes all postures \n            are supported.\n            '), '_add_to_posture_graph_if_parented': Tunable(description="\n            Whether or not the object should be added to the posture graph if it\n            is parented to an object that isn't a surface.  i.e. chairs that\n            should be seatable when slotted into a campfire (which isn't a surface)\n            ", tunable_type=bool, default=False), 'allow_preroll_multiple_targets': Tunable(description='\n            When checked allows multiple sims to target this object during \n            preroll autonomy. If not checked then the default preroll behavior\n            will happen.\n            \n            The default setting is to only allow each target to be targeted\n            once during preroll. However it makes sense in certain cases where\n            multiple sims can use the same object at the same time to allow\n            multiple targets.\n            ', tunable_type=bool, default=False), 'icon_override': OptionalTunable(description='\n            If enabled, the icon that will be displayed in the UI for this object.\n            This does not override the build/buy icon, which can be overriden\n            through the catalog.\n            ', tunable=TunableResourceKey(tuning_group=GroupNames.UI, resource_types=sims4.resources.CompoundTypes.IMAGE)), 'flammable_area': TunableFlammableAreaVariant(description='\n            How the object defines its area of flammability. This is used \n            by the fire service to build the quadtree of flammable objects.\n            '), '_provided_mobile_posture_affordances': OptionalTunable(description="\n            If enabled, this object will add these postures to the posture\n            graph. We need to do this for mobile postures that have no body\n            target and we don't intend on them ever being included in searches\n            for getting from one place to another without this object somewhere\n            on the lot.\n            ", tunable=TunableSet(description='\n                The set of mobile posture providing interactions we want this\n                object to provide.\n                ', tunable=TunableReference(description='\n                    The posture providing interaction we want to add to the\n                    posture graph when this object is instanced.\n                    ', manager=services.affordance_manager(), pack_safe=True, class_restrictions='SuperInteraction'), minlength=1)), 'recycling_data': TunableTuple(description='\n            Recycling information for this object.\n            ', recycling_values=TunableMapping(description='\n                Maps a buck type to the recycled value for this object.\n                ', key_type=TunableEnumEntry(tunable_type=BucksType, default=BucksType.INVALID, invalid_enums=BucksType.INVALID, pack_safe=True), key_name='Bucks Type', value_type=TunableRange(description='\n                    Object multiplier for this buck type.\n                    ', tunable_type=float, default=1.0, minimum=0.0), value_name='Value'), recycling_loot=TunableList(description='\n                Loot Actions that will be given when the object is recycled.\n                SingleActorAndObjectResolver will be used where actor is specified\n                by subject, and object is the object being recycled.\n                ', tunable=TunableReference(description='\n                    A loot action applied.\n                    ', manager=services.get_instance_manager(sims4.resources.Types.ACTION), pack_safe=True))), 'tests_to_bypass_utility_requirement': TunableMapping(description='\n            A mapping of utility types to tunable test sets. \n            ', key_type=TunableEnumEntry(tunable_type=Utilities, default=Utilities.POWER), value_type=TunableTestSet(description='\n                A test set to run when the object is the target of a\n                recipe or interaction and the utility required for that recipe or \n                interaction is absent. If at least one test group passes, \n                then any interaction or recipe that requires the utility\n                will be allowed to run despite the absence of the utility. \n                \n                ORs of ANDs.\n                '))}

    @classmethod
    def _verify_tuning_callback(cls):
        if cls._provided_mobile_posture_affordances is not None:
            for affordance in cls._provided_mobile_posture_affordances:
                if affordance.provided_posture_type is None:
                    logger.error("{} provides posture affordance {} but it doesn't provide a posture.", cls, affordance, owner='rmccord')
                elif not affordance.provided_posture_type.unconstrained:
                    logger.error('{} provides posture affordance {} but the provided posture is not unconstrained and therefore requires a body target.', cls, affordance, owner='rmccord')
                elif affordance in PostureGraphService.POSTURE_PROVIDING_AFFORDANCES:
                    logger.error('{} provides posture affordance {} but this is already provided by the posture graph in Posture Providing Affordances.', cls, affordance, owner='rmccord')

    def __init__(self, definition, **kwargs):
        super().__init__(definition, **kwargs)
        self._on_location_changed_callbacks = None
        self._transient = None
        self._created_constraints = None
        self._created_constraints_dirty = True
        self._household_owner_id = None
        self.new_in_inventory = True
        self.is_new_object = False
        self._provided_surface = UNSET
        zone = services.current_zone()
        account_id = build_buy.get_user_in_build_buy(zone.id)
        if account_id is not None:
            self.set_household_owner_id(zone.lot.owner_household_id)
            self.set_post_bb_fixup_needed()
            zone.set_to_fixup_on_build_buy_exit(self)
        self._hidden_flags = 0
        self._local_tags = None
        self._persisted_tags = None
        self._is_surface = None
        self._build_buy_use_flags = 0
        self._scheduled_elements = None
        self._work_locks = WeakSet()
        self._on_hidden_or_shown_callbacks = None

    def add_work_lock(self, handle):
        self._work_locks.add(handle)

    def remove_work_lock(self, handle):
        self._work_locks.discard(handle)

    @property
    def has_work_locks(self):
        if self._work_locks:
            return True
        return False

    @property
    def is_fire_related_object(self):
        if self.is_sim:
            return False
        return self.fire_retardant or self.flammable

    @constproperty
    def is_valid_for_height_checks():
        return True

    def has_tag(self, tag):
        if self._local_tags and tag in self._local_tags:
            return True
        if self._persisted_tags and tag in self._persisted_tags:
            return True
        return self.definition.has_build_buy_tag(tag)

    def has_any_tag(self, tags):
        return any(self.has_tag(tag) for tag in tags)

    def get_tags(self):
        tags = frozenset(self.definition.build_buy_tags)
        if self._local_tags:
            tags |= self._local_tags
        return tags

    def append_tags(self, tag_set, persist=False):
        if self.manager is not None:
            self.manager.add_tags_and_object_to_cache(tag_set, self)
        if self._local_tags:
            self._local_tags = self._local_tags | tag_set
        else:
            self._local_tags = tag_set
        if persist:
            if self._persisted_tags:
                self._persisted_tags = self._persisted_tags | tag_set
            else:
                self._persisted_tags = tag_set

    def get_icon_info_data(self):
        return IconInfoData(obj_instance=self, obj_def_id=self.definition.id, obj_geo_hash=self.geometry_state, obj_material_hash=self.material_hash, obj_name=LocalizationHelperTuning.get_object_name(self))

    @property
    def catalog_name(self):
        return get_object_catalog_name(self.definition.id)

    @property
    def catalog_description(self):
        return get_object_catalog_description(self.definition.id)

    def is_routing_surface_overlapped_at_position(self, position):
        routing_surface = self.provided_routing_surface
        if routing_surface is not None:
            (_, object_id) = services.terrain_service.terrain_object().get_routing_surface_height_and_surface_object_at(position.x, position.z, routing_surface)
            if object_id == self.id:
                return False
        return True

    @property
    def provided_routing_surface(self):
        if self._provided_surface is UNSET:
            self._provided_surface = None
            if self.routing_surface is not None:
                if self.has_component(FOOTPRINT_COMPONENT):
                    if placement.has_object_surface_footprint(self.get_footprint()):
                        self._provided_surface = routing.SurfaceIdentifier(services.current_zone_id(), self.routing_surface.secondary_id, routing.SurfaceType.SURFACETYPE_OBJECT)
        return self._provided_surface

    def get_icon_override(self):
        for icon_override in self._icon_override_gen():
            if icon_override is not None:
                return icon_override

    @forward_to_components_gen
    def _icon_override_gen(self):
        if self.icon_override is not None:
            yield self.icon_override

    @forward_to_components
    def populate_localization_token(self, token):
        self.definition.populate_localization_token(token)

    def is_hidden(self, allow_hidden_flags=0):
        if int(self._hidden_flags) & ~int(allow_hidden_flags):
            return True
        return False

    def has_hidden_flags(self, hidden_flags):
        if int(self._hidden_flags) & int(hidden_flags):
            return True
        return False

    def hide(self, hidden_reasons_to_add):
        self._hidden_flags = self._hidden_flags | hidden_reasons_to_add
        if self._on_hidden_or_shown_callbacks is not None:
            self._on_hidden_or_shown_callbacks(self, hidden_reasons_to_add, added=True)

    def show(self, hidden_reasons_to_remove):
        self._hidden_flags = self._hidden_flags & ~hidden_reasons_to_remove
        if self._on_hidden_or_shown_callbacks is not None:
            self._on_hidden_or_shown_callbacks(self, hidden_reasons_to_remove, added=False)

    @property
    def transient(self):
        if self._transient is not None:
            return self._transient
        return self._transient_tuning

    @transient.setter
    def transient(self, value):
        self._transient = value

    @distributor.fields.Field(op=distributor.ops.SetBuildBuyUseFlags)
    def build_buy_use_flags(self):
        return self._build_buy_use_flags

    @build_buy_use_flags.setter
    def build_buy_use_flags(self, value):
        self._build_buy_use_flags = value

    @distributor.fields.Field(op=distributor.ops.SetOwnerId)
    def household_owner_id(self):
        return self._household_owner_id

    _resend_household_owner_id = household_owner_id.get_resend()

    def get_edges(self):
        (lower_bound, upper_bound) = self.get_fooptrint_polygon_bounds()
        if lower_bound is None or upper_bound is None:
            return ()
        y = self.position.y
        transform = self.transform
        p0 = transform.transform_point(sims4.math.Vector3(lower_bound.x, y, lower_bound.z))
        p1 = transform.transform_point(sims4.math.Vector3(lower_bound.x, y, upper_bound.z))
        p2 = transform.transform_point(sims4.math.Vector3(upper_bound.x, y, upper_bound.z))
        p3 = transform.transform_point(sims4.math.Vector3(upper_bound.x, y, lower_bound.z))
        return ((p0, p1), (p1, p2), (p2, p3), (p3, p0))

    def get_edge_constraint(self, constraint_width=1.0, inward_dir=False, return_constraint_list=False, los_reference_point=DEFAULT, sim=None):
        edges = self.get_edges()
        polygons = []
        for (start, stop) in edges:
            along = sims4.math.vector_normalize(stop - start)
            inward = sims4.math.vector3_rotate_axis_angle(along, sims4.math.PI/2, sims4.math.Vector3.Y_AXIS())
            if inward_dir:
                polygon = sims4.geometry.Polygon([start, start + constraint_width*inward, stop + constraint_width*inward, stop])
            else:
                polygon = sims4.geometry.Polygon([start, stop, stop - constraint_width*inward, start - constraint_width*inward])
            polygons.append(polygon)
        routing_surface = self.routing_surface
        if return_constraint_list:
            constraint_list = []
            for polygon in polygons:
                restricted_polygon = sims4.geometry.RestrictedPolygon(polygon, ())
                constraint = Constraint(routing_surface=routing_surface, geometry=restricted_polygon, los_reference_point=los_reference_point, posture_state_spec=STAND_AT_NONE_POSTURE_STATE_SPEC)
                constraint_list.append(constraint)
            return constraint_list
        else:
            geometry = sims4.geometry.RestrictedPolygon(sims4.geometry.CompoundPolygon(polygons), ())
            constraint = Constraint(routing_surface=routing_surface, geometry=geometry, posture_state_spec=STAND_AT_NONE_POSTURE_STATE_SPEC)
            return constraint

    def get_created_constraint(self, tuned_constraint):
        if not self.additional_interaction_constraints:
            return
        if not self._created_constraints:
            self._created_constraints = {}
        if self._created_constraints_dirty:
            self._created_constraints.clear()
            for tuned_additional_constraint in self.additional_interaction_constraints:
                constraint = tuned_additional_constraint.constraint
                if constraint is not None:
                    self._created_constraints[constraint] = constraint.create_constraint(None, self)
            self._created_constraints_dirty = False
        return self._created_constraints.get(tuned_constraint)

    @forward_to_components
    def register_rebate_tests(self, test_set):
        pass

    @forward_to_components
    def validate_definition(self):
        pass

    def _should_invalidate_location(self):
        parent = self.parent
        if parent is None:
            return True
        return parent._should_invalidate_location()

    def _notify_buildbuy_of_location_change(self, old_location):
        if self.persistence_group == PersistenceGroups.OBJECT and self._should_invalidate_location():
            invalidate_object_location(self.id)

    def set_build_buy_lockout_state(self, lockout_state, lockout_timer=None):
        if self._build_buy_lockout_alarm_handler is not None:
            alarms.cancel_alarm(self._build_buy_lockout_alarm_handler)
            self._build_buy_lockout_alarm_handler = None
        elif self._build_buy_lockout and lockout_state:
            return
        if lockout_state:
            if lockout_timer is not None:
                time_span_real_time = clock.interval_in_real_seconds(lockout_timer)
                self._build_buy_lockout_alarm_handler = alarms.add_alarm_real_time(self, time_span_real_time, lambda *_: self.set_build_buy_lockout_state(False))
        if lockout_state and not self.build_buy_lockout:
            self.reset(ResetReason.RESET_EXPECTED)
        self._build_buy_lockout = lockout_state
        self.resend_interactable()
        self.resend_tint()

    def on_location_changed(self, old_location):
        super().on_location_changed(old_location)
        self.mark_get_locations_for_posture_needs_update()
        self.clear_check_line_of_sight_cache()
        self._provided_surface = UNSET
        if self.id:
            self._update_persistence_group()
            self._notify_buildbuy_of_location_change(old_location)
            self.manager.on_location_changed(self)
            if self._on_location_changed_callbacks is not None:
                self._on_location_changed_callbacks(self, old_location, self.location)
            self._created_constraints_dirty = True

    def set_object_def_state_index(self, state_index):
        if type(self) != self.get_class_for_obj_state(state_index):
            logger.error("Attempting to change object {}'s state to one that would require a different runtime class.  This is not supported.", self, owner='tastle')
        self.apply_definition(self.definition, state_index)
        self.model = self._model
        self.rig = self._rig
        self.resend_state_index()
        self.resend_slot()

    def register_on_location_changed(self, callback):
        if self._on_location_changed_callbacks is None:
            self._on_location_changed_callbacks = CallableListConsumingExceptions()
        self._on_location_changed_callbacks.append(callback)

    def unregister_on_location_changed(self, callback):
        if self._on_location_changed_callbacks is None:
            logger.error('Unregistering location changed callback on {} when there are none registered.', self)
            return
        if callback not in self._on_location_changed_callbacks:
            logger.error('Unregistering location changed callback on {} that is not registered. Callback: {}.', self, callback)
            return
        self._on_location_changed_callbacks.remove(callback)
        if not self._on_location_changed_callbacks:
            self._on_location_changed_callbacks = None

    def is_on_location_changed_callback_registered(self, callback):
        return callback in self._on_location_changed_callbacks

    def register_on_hidden_or_shown(self, callback):
        if self._on_hidden_or_shown_callbacks is None:
            self._on_hidden_or_shown_callbacks = CallableListConsumingExceptions()
        self._on_hidden_or_shown_callbacks.append(callback)

    def unregister_on_hidden_or_shown(self, callback):
        if self._on_hidden_or_shown_callbacks is None:
            logger.error('Unregistering hidden or shown callback on {} when there are none registered.', self)
            return
        if callback not in self._on_hidden_or_shown_callbacks:
            logger.error('Unregistering hidden or shown callback on {} that is not registered. Callback: {}.', self, callback)
            return
        self._on_hidden_or_shown_callbacks.remove(callback)
        if not self._on_hidden_or_shown_callbacks:
            self._on_hidden_or_shown_callbacks = None

    def is_on_hidden_or_shown_callback_registered(self, callback):
        if self._on_hidden_or_shown_callbacks is None:
            return False
        return callback in self._on_hidden_or_shown_callbacks

    def is_on_active_lot(self, tolerance=0):
        return self.persistence_group == PersistenceGroups.OBJECT

    @property
    def is_in_navmesh(self):
        if self._routing_context is not None and self._routing_context.object_footprint_id is not None:
            return True
        else:
            return False

    @property
    def may_move(self):
        return self.vehicle_component is not None or self.routing_component is not None and self.routing_component.object_routing_component is not None

    def get_surface_override_for_posture(self, source_posture_name):
        pass

    @property
    def add_to_posture_graph_if_parented(self):
        return self._add_to_posture_graph_if_parented

    @classproperty
    def provided_mobile_posture_affordances(cls):
        return cls._provided_mobile_posture_affordances or EMPTY_SET

    def get_joint_transform_for_joint(self, joint_name):
        transform = get_joint_transform_from_rig(self.rig, joint_name)
        transform = Transform.concatenate(transform, self.transform)
        return transform

    @property
    def object_radius(self):
        if self._routing_context is None:
            return routing.get_default_agent_radius()
        return self._routing_context.object_radius

    @property
    def persistence_group(self):
        return self._persistence_group

    @persistence_group.setter
    def persistence_group(self, value):
        self._persistence_group = value

    def _update_persistence_group(self):
        if self.is_in_inventory():
            self.persistence_group = objects.persistence_groups.PersistenceGroups.OBJECT
            return
        if self.persistence_group == objects.persistence_groups.PersistenceGroups.OBJECT:
            if not services.current_zone().lot.is_position_on_lot(self.position, 0):
                remove_object_from_buildbuy_system(self.id)
                self.persistence_group = objects.persistence_groups.PersistenceGroups.IN_OPEN_STREET
        elif self.persistence_group == objects.persistence_groups.PersistenceGroups.IN_OPEN_STREET and services.current_zone().lot.is_position_on_lot(self.position, 0):
            self.persistence_group = objects.persistence_groups.PersistenceGroups.OBJECT
            add_object_to_buildbuy_system(self.id)

    def _fixup_pool_surface(self):
        if (self.item_location == ItemLocation.FROM_WORLD_FILE or self.item_location == ItemLocation.FROM_CONDITIONAL_LAYER) and (self.routing_surface.type != SurfaceType.SURFACETYPE_POOL and build_buy.PlacementFlags.REQUIRES_WATER_SURFACE & build_buy.get_object_placement_flags(self.definition.id)) and get_water_depth_at_location(self.location) > 0:
            routing_surface = self.routing_surface
            self.set_location(self.location.clone(routing_surface=SurfaceIdentifier(routing_surface.primary_id, routing_surface.secondary_id, SurfaceType.SURFACETYPE_POOL)))

    def _add_to_world(self):
        if self.persistence_group == PersistenceGroups.OBJECT:
            add_object_to_buildbuy_system(self.id)

    def _remove_from_world(self):
        if self.persistence_group == PersistenceGroups.OBJECT:
            remove_object_from_buildbuy_system(self.id)

    def on_add(self):
        super().on_add()
        self._add_to_world()
        self.register_on_location_changed(self._location_changed)
        if self.is_fire_related_object:
            fire_service = services.get_fire_service()
            self.register_on_location_changed(fire_service.flammable_object_location_changed)
        posture_graph_service = services.posture_graph_service()
        if posture_graph.is_object_mobile_posture_compatible(self):
            self.register_on_location_changed(posture_graph_service.mobile_posture_object_location_changed)
        if self.provided_mobile_posture_affordances:
            posture_graph_service.add_mobile_posture_provider(self)
        services.call_to_action_service().object_created(self)
        self.try_mark_as_new_object()

    def on_remove(self):
        zone = services.current_zone()
        if zone is not None and not zone.is_zone_shutting_down:
            services.get_event_manager().process_event(test_events.TestEvent.ObjectDestroyed, obj=self)
        super().on_remove()
        if not zone.is_zone_shutting_down:
            self._remove_from_world()
            self.unregister_on_location_changed(self._location_changed)
            if self.is_fire_related_object:
                fire_service = services.get_fire_service()
                if fire_service is not None:
                    self.unregister_on_location_changed(fire_service.flammable_object_location_changed)
            posture_graph_service = services.posture_graph_service()
            if self.provided_mobile_posture_affordances:
                posture_graph_service.remove_mobile_posture_provider(self)
            if posture_graph.is_object_mobile_posture_compatible(self):
                posture_graph_service.remove_object_from_mobile_posture_quadtree(self)
                self.unregister_on_location_changed(posture_graph_service.mobile_posture_object_location_changed)
        else:
            self._on_location_changed_callbacks = None

    def on_added_to_inventory(self):
        super().on_added_to_inventory()
        self._remove_from_world()
        self.visibility = VisibilityState(False)

    def on_removed_from_inventory(self):
        super().on_removed_from_inventory()
        self._add_to_world()
        self.visibility = VisibilityState(True)

    @forward_to_components
    def on_buildbuy_exit(self):
        self._update_location_callbacks(update_surface=True)

    def _update_location_callbacks(self, update_surface=False):
        self._inside_status_change()
        self._natural_ground_status_change()
        if update_surface:
            self._surface_type_changed()

    @staticmethod
    def _location_changed(obj, old_loc, new_loc):
        if obj.zone_id:
            obj._update_location_callbacks(update_surface=old_loc.routing_surface != new_loc.routing_surface)
        obj._fixup_pool_surface()

    @forward_to_components
    def _surface_type_changed(self):
        pass

    def _inside_status_change(self, *_, **__):
        if self.is_outside:
            self._set_placed_outside()
        else:
            self._set_placed_inside()

    def _natural_ground_status_change(self, *_, **__):
        if self.routing_surface is not None and self.routing_surface.type == SurfaceType.SURFACETYPE_POOL:
            return
        if self.is_on_natural_ground():
            self._set_placed_on_natural_ground()
        else:
            self._set_placed_off_natural_ground()

    @forward_to_components
    def _set_placed_outside(self):
        pass

    @forward_to_components
    def _set_placed_inside(self):
        pass

    @forward_to_components
    def _set_placed_on_natural_ground(self):
        pass

    @forward_to_components
    def _set_placed_off_natural_ground(self):
        pass

    @ored_forward_to_components
    def on_hovertip_requested(self):
        return False

    @property
    def is_outside(self):
        routing_surface = self.routing_surface
        level = 0 if routing_surface is None else routing_surface.secondary_id
        try:
            return is_location_outside(self.position, level)
        except RuntimeError:
            pass

    def is_on_natural_ground(self):
        if self.parent is not None:
            return False
        routing_surface = self.routing_surface
        level = 0 if routing_surface is None else routing_surface.secondary_id
        try:
            return is_location_natural_ground(self.position, level)
        except RuntimeError:
            pass

    def try_mark_as_new_object(self):
        if not (self.should_mark_as_new and services.current_zone().is_in_build_buy):
            return
        self.add_dynamic_component(objects.components.types.NEW_OBJECT_COMPONENT)

    def on_child_added(self, child, location):
        super().on_child_added(child, location)
        self.get_raycast_root().on_leaf_child_changed()

    def on_child_removed(self, child, new_parent=None):
        super().on_child_removed(child, new_parent=new_parent)
        self.get_raycast_root().on_leaf_child_changed()

    def on_leaf_child_changed(self):
        if self._raycast_context is not None:
            self._create_raycast_context()

    @property
    def forward_direction_for_picking(self):
        return sims4.math.Vector3.Z_AXIS()

    @property
    def route_target(self):
        parts = self.parts
        if parts is None:
            return (RouteTargetType.OBJECT, self)
        else:
            return (RouteTargetType.PARTS, parts)

    @property
    def should_mark_as_new(self):
        return True

    def is_surface(self, include_parts=False, ignore_deco_slots=False):
        if self._is_surface is None:
            self._is_surface = {}
        key = (include_parts, ignore_deco_slots)
        is_surface = self._is_surface.get(key)
        if is_surface is not None:
            return is_surface
        inventory_component = self.inventory_component
        if inventory_component is not None and inventory_component.has_get_put:
            self._is_surface[key] = True
            return True

        def is_valid_surface_slot(slot_type):
            if not (ignore_deco_slots and slot_type.is_deco_slot) and slot_type.is_surface:
                return True
            return False

        for runtime_slot in self.get_runtime_slots_gen():
            if not include_parts and runtime_slot.owner is not self:
                continue
            if not any(is_valid_surface_slot(slot_type) for slot_type in runtime_slot.slot_types):
                continue
            if not runtime_slot.owner.is_same_object_or_part(self):
                continue
            self._is_surface[key] = True
            return True
        else:
            self._is_surface[key] = False
            return False

    def get_save_lot_coords_and_level(self):
        lot_coord_msg = LotCoord()
        parent = self.parent
        if parent is not None and parent.is_sim:
            parent.force_update_routing_location()
            starting_position = parent.position + parent.forward
            starting_location = placement.create_starting_location(position=starting_position, orientation=parent.orientation, routing_surface=self.location.world_routing_surface)
            fgl_context = placement.create_fgl_context_for_object(starting_location, self)
            (trans, orient) = placement.find_good_location(fgl_context)
            if trans is None:
                logger.warn('Unable to find good location to save object{}, which is parented to sim {} and cannot go into an inventory. Defaulting to location of sim.', self, parent)
                transform = parent.transform
            else:
                transform = sims4.math.Transform(trans, orient)
            if self.persistence_group == PersistenceGroups.OBJECT:
                transform = services.current_zone().lot.convert_to_lot_coordinates(transform)
        elif self.persistence_group == PersistenceGroups.OBJECT:
            transform = services.current_zone().lot.convert_to_lot_coordinates(self.transform)
        else:
            transform = self.transform
        lot_coord_msg.x = transform.translation.x
        lot_coord_msg.y = transform.translation.y
        lot_coord_msg.z = transform.translation.z
        lot_coord_msg.rot_x = transform.orientation.x
        lot_coord_msg.rot_y = transform.orientation.y
        lot_coord_msg.rot_z = transform.orientation.z
        lot_coord_msg.rot_w = transform.orientation.w
        if self.location.world_routing_surface is not None:
            level = self.location.level
        else:
            level = 0
        return (lot_coord_msg, level)

    def save_object(self, object_list, *args, **kwargs):
        save_data = super().save_object(object_list, *args, **kwargs)
        if save_data is None:
            return
        save_data.slot_id = self.bone_name_hash
        (save_data.position, save_data.level) = self.get_save_lot_coords_and_level()
        inventory_plex_id = self.get_inventory_plex_id()
        if inventory_plex_id is not None:
            save_data.inventory_plex_id = inventory_plex_id
        save_data.scale = self.scale
        save_data.state_index = self.state_index
        if hasattr(save_data, 'buildbuy_use_flags'):
            save_data.buildbuy_use_flags = self._build_buy_use_flags
        save_data.cost = self.base_value
        save_data.ui_metadata = self.ui_metadata._value
        self.post_tooltip_save_data_stored()
        save_data.is_new = self.new_in_inventory
        save_data.is_new_object = self.is_new_object
        self.populate_icon_canvas_texture_info(save_data)
        if self._household_owner_id is not None:
            save_data.owner_id = self._household_owner_id
        save_data.needs_depreciation = self._needs_depreciation
        save_data.needs_post_bb_fixup = self._needs_post_bb_fixup
        if self._persisted_tags:
            save_data.persisted_tags.extend(self._persisted_tags)
        if self._multicolor is not None:
            for color in self._multicolor:
                color = getattr(color, 'value', color)
                multicolor_info_msg = save_data.multicolor.add()
                (multicolor_info_msg.x, multicolor_info_msg.y, multicolor_info_msg.z, _) = sims4.color.to_rgba(color)
        save_data.created_from_lot_template = False
        save_data.stack_sort_order = self.get_stack_sort_order()
        if self.material_state:
            save_data.material_state = self.material_state.state_name_hash
        if self.geometry_state:
            save_data.geometry_state = self.geometry_state
        if self.model:
            model_key = sims4.resources.get_protobuff_for_key(self.model)
            save_data.model_override_resource_key = model_key
        parent = self.bb_parent
        if parent is not None:
            if not parent.is_sim:
                save_data.parent_id = parent.id
        if not (parent is None or not parent.is_sim):
            save_data.object_parent_type = self._parent_type
            save_data.encoded_parent_location = self._parent_location
        inventory = self.inventory_component
        if inventory is not None:
            if not inventory.is_shared_inventory:
                save_data.unique_inventory = inventory.save_items()
        return save_data

    def load_object(self, object_data, **kwargs):
        if object_data.HasField('owner_id'):
            self._household_owner_id = object_data.owner_id
        if self.is_downloaded:
            self.base_value = self.catalog_value
        else:
            self.base_value = object_data.cost
        self.new_in_inventory = object_data.is_new
        super().load_object(object_data, **kwargs)
        if object_data.HasField('texture_id') and self.canvas_component is not None:
            self.canvas_component.set_painting_texture_id(object_data.texture_id)
        if object_data.HasField('needs_depreciation'):
            self._needs_depreciation = object_data.needs_depreciation
        if object_data.HasField('needs_post_bb_fixup'):
            self._needs_post_bb_fixup = object_data.needs_post_bb_fixup
        else:
            self._needs_post_bb_fixup = self._needs_depreciation
        inventory = self.inventory_component
        if inventory is not None:
            inventory.load_items(object_data.unique_inventory)
        if sims4.protocol_buffer_utils.has_field(object_data, 'buildbuy_use_flags'):
            self._build_buy_use_flags = object_data.buildbuy_use_flags
        self.is_new_object = object_data.is_new_object
        if self.is_new_object:
            self.add_dynamic_component(objects.components.types.NEW_OBJECT_COMPONENT)
        if object_data.persisted_tags is not None:
            self.append_tags(set(object_data.persisted_tags))

    def finalize(self, **kwargs):
        super().finalize(**kwargs)
        self.try_post_bb_fixup(**kwargs)
        if self.is_fire_related_object:
            fire_service = services.get_fire_service()
            if fire_service is not None:
                fire_service.flammable_object_location_changed(self)
        if posture_graph.is_object_mobile_posture_compatible(self):
            posture_graph_service = services.current_zone().posture_graph_service
            posture_graph_service.mobile_posture_object_location_changed(self)

    def set_household_owner_id(self, new_owner_id):
        self._household_owner_id = new_owner_id
        self._resend_household_owner_id()
        if self.live_drag_component is not None:
            self.live_drag_component.resolve_live_drag_household_permission()

    def get_household_owner_id(self):
        return self._household_owner_id

    def get_object_property(self, property_type):
        if property_type == GameObjectProperty.CATALOG_PRICE:
            return self.definition.price
        if property_type == GameObjectProperty.MODIFIED_PRICE:
            return self.current_value
        if property_type == GameObjectProperty.RARITY:
            return self.get_object_rarity_string()
        if property_type == GameObjectProperty.GENRE:
            return Genre.get_genre_localized_string(self)
        if property_type == GameObjectProperty.RECIPE_NAME or property_type == GameObjectProperty.RECIPE_DESCRIPTION:
            return self.get_craftable_property(self, property_type)
        if property_type == GameObjectProperty.OBJ_TYPE_REL_ID:
            return services.relationship_service().get_object_type_rel_id(self)
        logger.error('Requested property_type {} not found on game_object'.format(property_type), owner='camilogarcia')

    def update_ownership(self, sim, make_sim_owner=True):
        household_id = sim.household_id
        if self._household_owner_id != household_id:
            if self.ownable_component is not None:
                self.ownable_component.update_sim_ownership(None)
            self.set_household_owner_id(household_id)
        if make_sim_owner and self.ownable_component is not None:
            self.ownable_component.update_sim_ownership(sim.sim_id)

    @property
    def flammable(self):
        fire_service = services.get_fire_service()
        if fire_service is not None:
            return fire_service.is_object_flammable(self)
        return False

    def object_bounds_for_flammable_object(self, fire_retardant_bonus):
        return self.flammable_area.get_bounds_for_flammable_object(self, fire_retardant_bonus)

    @property
    def is_set_as_head(self):
        parent = self.parent
        if parent is None:
            return False
        if not parent.is_sim:
            return False
        if parent.current_object_set_as_head is None:
            return False
        else:
            parent_head = parent.current_object_set_as_head()
            if not self.is_same_object_or_part(parent_head):
                return False
        return True

    @classmethod
    def register_tuned_animation(cls, *_, **__):
        pass

    @classmethod
    def add_auto_constraint(cls, *_, **__):
        pass

    def may_reserve(self, sim, *args, **kwargs):
        for child in self.children:
            child_targets = child.parts if child.parts else (child,)
            for child_target in child_targets:
                if child_target.is_sim:
                    continue
                reserve_result = child_target.may_reserve(sim, *args, **kwargs)
                if not reserve_result:
                    return reserve_result
        return super().may_reserve(sim, *args, **kwargs)

    def make_transient(self):
        self.transient = True
        self._destroy_if_not_in_use()

    def _destroy_if_not_in_use(self):
        if self.is_part:
            self.part_owner._destroy_if_not_in_use()
            return
        if self.self_or_part_in_use:
            return
        if not self.transient:
            return
        self.schedule_destroy_asap(source=self, cause='Destroying unused transient object.')
        posture_graph_service = services.current_zone().posture_graph_service
        if posture_graph_service.is_object_pending_deletion(self):
            posture_graph_service.finalize_object_deletion(self)

    def remove_reservation_handler(self, *args, **kwargs):
        super().remove_reservation_handler(*args, **kwargs)
        self._destroy_if_not_in_use()

    def schedule_element(self, timeline, element):
        resettable_element = reset.ResettableElement(element, self)
        resettable_element.on_scheduled(timeline)
        timeline.schedule(resettable_element)
        return resettable_element

    def register_reset_element(self, element):
        if self._scheduled_elements is None:
            self._scheduled_elements = set()
        self._scheduled_elements.add(element)

    def unregister_reset_element(self, element):
        if self._scheduled_elements is not None:
            self._scheduled_elements.discard(element)
            if not self._scheduled_elements:
                self._scheduled_elements = None

    def on_reset_element_hard_stop(self):
        self.reset(reset_reason=ResetReason.RESET_EXPECTED)

    def on_reset_get_elements_to_hard_stop(self, reset_reason):
        elements_to_reset = super().on_reset_get_elements_to_hard_stop(reset_reason)
        if self._scheduled_elements is not None:
            scheduled_elements = list(self._scheduled_elements)
            self._scheduled_elements = None
            for element in scheduled_elements:
                elements_to_reset.append(element)
                element.unregister()
        return elements_to_reset

    def get_gsi_portal_items(self, key_name, value_name):
        household_owner_id = self.household_owner_id
        household_owner = services.household_manager().get(household_owner_id)
        name = household_owner.name if household_owner is not None else 'Not Owned'
        return [{key_name: 'Household Owner', value_name: name}]
Exemplo n.º 10
0
 def __init__(self,
              constraint_locked_args=frozendict(),
              circle_locked_args=frozendict(),
              disabled_constraints=(),
              default='circle',
              **kwargs):
     if not circle_locked_args:
         circle_locked_args = constraint_locked_args
     else:
         circle_locked_args.update(constraint_locked_args)
     available_constraints = {
         'facing':
         TunableFacing(
             description=
             '\n                Existential tunable that requires the sim to face the object.\n                '
         ),
         'line_of_sight':
         TunableLineOfSight(
             description=
             '\n                Existential tunable that creates a line of sight constraint.\n                ',
             locked_args=constraint_locked_args),
         'cone':
         TunableCone(
             description=
             '\n                The relative cone geometry required for a sim/posture to use the object.\n                ',
             min_radius=0,
             max_radius=1,
             angle=sims4.math.PI,
             locked_args=constraint_locked_args),
         'circle':
         TunableCircle(
             description=
             '\n                The relative circle geometry required for a sim/posture to use the object.',
             radius=1,
             locked_args=circle_locked_args),
         'spawn_points':
         TunableSpawnPoint(
             description=
             '\n                A constraint that represents all of the spawn locations on the lot.\n                '
         ),
         'spawn_points_with_backup':
         TunableSpawnPointWithBackup(
             description=
             '\n                A constraint that represents primary spawn points with backup secondary ones.\n                '
         ),
         'relative_circle':
         RelativeCircleConstraint.TunableFactory(
             locked_args=constraint_locked_args),
         'current_position':
         CurrentPosition.TunableFactory(),
         'portal':
         PortalConstraint.TunableFactory(),
         'position':
         TunablePosition(
             description=
             '\n                The relative position geometry required for a sim/posture to use the object.\n                ',
             relative_position=sims4.math.Vector3(0, 0, 0)),
         'water_depth':
         WaterDepthConstraint.TunableFactory(),
         'water_depth_interval':
         WaterDepthIntervalConstraint.TunableFactory(),
         'terrain_material':
         TerrainMaterialConstraint.TunableFactory(),
         'ocean_loc':
         TunableOceanStartLocationConstraint(
             description=
             '\n                The circle geometry relative to the nearest ocean locator.',
             radius=1,
             locked_args=circle_locked_args),
         'default':
         default
     }
     for disabled_name in disabled_constraints:
         if disabled_name in available_constraints:
             del available_constraints[disabled_name]
     kwargs.update(available_constraints)
     super().__init__(**kwargs)
Exemplo n.º 11
0
 def _create_costs(self, default_cost, costs=()):
     costs = dict(costs)
     costs[PostureOperation.DEFAULT_COST_KEY] = default_cost
     return frozendict(costs)
Exemplo n.º 12
0
from _sims4_collections import frozendict
from sims4.common import Pack, is_available_pack
import enum

class SimInfoGameplayOptions(enum.IntFlags):
    ALLOW_FAME = 1
    ALLOW_REPUTATION = 2
    FORCE_CURRENT_ALLOW_FAME_SETTING = 4
    FREEZE_FAME = 8

REQUIRED_PACK_BY_OPTION = frozendict({SimInfoGameplayOptions.ALLOW_FAME: Pack.EP06, SimInfoGameplayOptions.ALLOW_REPUTATION: Pack.EP06, SimInfoGameplayOptions.FORCE_CURRENT_ALLOW_FAME_SETTING: Pack.EP06, SimInfoGameplayOptions.FREEZE_FAME: Pack.EP06})

def is_required_pack_installed(sim_info_gameplay_option):
    pack = REQUIRED_PACK_BY_OPTION.get(sim_info_gameplay_option, None)
    if pack is None:
        return True
    return is_available_pack(pack)
Exemplo n.º 13
0
 def _define_supported_postures(cls):
     return frozendict({
         ParticipantType.Actor:
         STAND_NO_CARRY_NO_SURFACE_POSTURE_MANIFEST
     })
Exemplo n.º 14
0
class Trait(HasTunableReference,
            SuperAffordanceProviderMixin,
            TargetSuperAffordanceProviderMixin,
            HasTunableLodMixin,
            MixerActorMixin,
            MixerProviderMixin,
            metaclass=HashedTunedInstanceMetaclass,
            manager=services.trait_manager()):
    EQUIP_SLOT_NUMBER_MAP = TunableMapping(
        description=
        '\n        The number of personality traits available to Sims of specific ages.\n        ',
        key_type=TunableEnumEntry(
            description="\n            The Sim's age.\n            ",
            tunable_type=sim_info_types.Age,
            default=sim_info_types.Age.YOUNGADULT),
        value_type=Tunable(
            description=
            '\n            The number of personality traits available to a Sim of the specified\n            age.\n            ',
            tunable_type=int,
            default=3),
        key_name='Age',
        value_name='Slot Number')
    PERSONALITY_TRAIT_TAG = TunableEnumEntry(
        description=
        '\n        The tag that marks a trait as a personality trait.\n        ',
        tunable_type=tag.Tag,
        default=tag.Tag.INVALID,
        invalid_enums=(tag.Tag.INVALID, ))
    DAY_NIGHT_TRACKING_BUFF_TAG = TunableEnumWithFilter(
        description=
        '\n        The tag that marks buffs as opting in to Day Night Tracking on traits..\n        ',
        tunable_type=tag.Tag,
        filter_prefixes=['buff'],
        default=tag.Tag.INVALID,
        invalid_enums=(tag.Tag.INVALID, ))
    INSTANCE_TUNABLES = {
        'trait_type':
        TunableEnumEntry(
            description='\n            The type of the trait.\n            ',
            tunable_type=TraitType,
            default=TraitType.PERSONALITY,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.APPEARANCE),
        'display_name':
        TunableLocalizedStringFactory(
            description=
            "\n            The trait's display name. This string is provided with the owning\n            Sim as its only token.\n            ",
            allow_none=True,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.APPEARANCE),
        'display_name_gender_neutral':
        TunableLocalizedString(
            description=
            "\n            The trait's gender-neutral display name. This string is not provided\n            any tokens, and thus can't rely on context to properly form\n            masculine and feminine forms.\n            ",
            allow_none=True,
            tuning_group=GroupNames.APPEARANCE),
        'trait_description':
        TunableLocalizedStringFactory(
            description="\n            The trait's description.\n            ",
            allow_none=True,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.APPEARANCE),
        'trait_origin_description':
        TunableLocalizedString(
            description=
            "\n            A description of how the Sim obtained this trait. Can be overloaded\n            for other uses in certain cases:\n            - When the trait type is AGENT this string is the name of the \n                agency's Trade type and will be provided with the owning sim \n                as its token.\n            - When the trait type is HIDDEN and the trait is used by the CAS\n                STORIES flow, this can be used as a secondary description in \n                the CAS Stories UI. If this trait is tagged as a CAREER CAS \n                stories trait, this description will be used to explain which \n                skills are also granted with this career.\n            ",
            allow_none=True,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.APPEARANCE),
        'icon':
        TunableResourceKey(
            description="\n            The trait's icon.\n            ",
            allow_none=True,
            resource_types=CompoundTypes.IMAGE,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.APPEARANCE),
        'pie_menu_icon':
        TunableResourceKey(
            description=
            "\n            The trait's pie menu icon.\n            ",
            resource_types=CompoundTypes.IMAGE,
            default=None,
            allow_none=True,
            tuning_group=GroupNames.APPEARANCE),
        'trait_asm_overrides':
        TunableTuple(
            description=
            '\n            Tunables that will specify if a Trait will add any parameters\n            to the Sim and how it will affect their boundary conditions.\n            ',
            param_type=OptionalTunable(
                description=
                '\n                Define if this trait is parameterized as an on/off value or as\n                part of an enumeration.\n                ',
                tunable=Tunable(
                    description=
                    '\n                    The name of the parameter enumeration. For example, if this\n                    value is tailType, then the tailType actor parameter is set\n                    to the value specified in param_value, for this Sim.\n                    ',
                    tunable_type=str,
                    default=None),
                disabled_name='boolean',
                enabled_name='enum'),
            trait_asm_param=Tunable(
                description=
                "\n                The ASM parameter for this trait. If unset, it will be auto-\n                generated depending on the instance name (e.g. 'trait_Clumsy').\n                ",
                tunable_type=str,
                default=None),
            consider_for_boundary_conditions=Tunable(
                description=
                '\n                If enabled the trait_asm_param will be considered when a Sim\n                is building the goals and validating against its boundary\n                conditions.\n                This should ONLY be enabled, if we need this parameter for\n                cases like a posture transition, or boundary specific cases. \n                On regular cases like an animation outcome, this is not needed.\n                i.e. Vampire trait has an isVampire parameter set to True, so\n                when animatin out of the coffin it does different get in/out \n                animations.  When this is enabled, isVampire will be set to \n                False for every other Sim.\n                ',
                tunable_type=bool,
                default=False),
            tuning_group=GroupNames.ANIMATION),
        'ages':
        TunableSet(
            description=
            '\n            The allowed ages for this trait. If no ages are specified, then all\n            ages are considered valid.\n            ',
            tunable=TunableEnumEntry(tunable_type=Age,
                                     default=None,
                                     export_modes=ExportModes.All),
            tuning_group=GroupNames.AVAILABILITY),
        'genders':
        TunableSet(
            description=
            '\n            The allowed genders for this trait. If no genders are specified,\n            then all genders are considered valid.\n            ',
            tunable=TunableEnumEntry(tunable_type=Gender,
                                     default=None,
                                     export_modes=ExportModes.All),
            tuning_group=GroupNames.AVAILABILITY),
        'species':
        TunableSet(
            description=
            '\n            The allowed species for this trait. If not species are specified,\n            then all species are considered valid.\n            ',
            tunable=TunableEnumEntry(tunable_type=Species,
                                     default=Species.HUMAN,
                                     invalid_enums=(Species.INVALID, ),
                                     export_modes=ExportModes.All),
            tuning_group=GroupNames.AVAILABILITY),
        'conflicting_traits':
        TunableList(
            description=
            '\n            Conflicting traits for this trait. If the Sim has any of the\n            specified traits, then they are not allowed to be equipped with this\n            one.\n            \n            e.g.\n             Family Oriented conflicts with Hates Children, and vice-versa.\n            ',
            tunable=TunableReference(manager=services.trait_manager(),
                                     pack_safe=True),
            export_modes=ExportModes.All,
            tuning_group=GroupNames.AVAILABILITY),
        'is_npc_only':
        Tunable(
            description=
            '\n            If checked, this trait will get removed from Sims that have a home\n            when the zone is loaded or whenever they switch to a household that\n            has a home zone.\n            ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.AVAILABILITY),
        'cas_selected_icon':
        TunableResourceKey(
            description=
            '\n            Icon to be displayed in CAS when this trait has already been applied\n            to a Sim.\n            ',
            resource_types=CompoundTypes.IMAGE,
            default=None,
            allow_none=True,
            export_modes=(ExportModes.ClientBinary, ),
            tuning_group=GroupNames.CAS),
        'cas_idle_asm_key':
        TunableInteractionAsmResourceKey(
            description=
            '\n            The ASM to use for the CAS idle.\n            ',
            default=None,
            allow_none=True,
            category='asm',
            export_modes=ExportModes.All,
            tuning_group=GroupNames.CAS),
        'cas_idle_asm_state':
        Tunable(
            description=
            '\n            The state to play for the CAS idle.\n            ',
            tunable_type=str,
            default=None,
            source_location='cas_idle_asm_key',
            source_query=SourceQueries.ASMState,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.CAS),
        'cas_trait_asm_param':
        Tunable(
            description=
            '\n            The ASM parameter for this trait for use with CAS ASM state machine,\n            driven by selection of this Trait, i.e. when a player selects the a\n            romantic trait, the Flirty ASM is given to the state machine to\n            play. The name tuned here must match the animation state name\n            parameter expected in Swing.\n            ',
            tunable_type=str,
            default=None,
            export_modes=ExportModes.All,
            tuning_group=GroupNames.CAS),
        'tags':
        TunableList(
            description=
            "\n            The associated categories of the trait. Need to distinguish among\n            'Personality Traits', 'Achievement Traits' and 'Walkstyle\n            Traits'.\n            ",
            tunable=TunableEnumEntry(tunable_type=tag.Tag,
                                     default=tag.Tag.INVALID),
            export_modes=ExportModes.All,
            tuning_group=GroupNames.CAS),
        'sim_info_fixup_actions':
        TunableList(
            description=
            '\n            A list of fixup actions which will be performed on a sim_info with\n            this trait when it is loaded.\n            ',
            tunable=TunableVariant(
                career_fixup_action=_SimInfoCareerFixupAction.TunableFactory(
                    description=
                    '\n                    A fix up action to set a career with a specific level.\n                    '
                ),
                skill_fixup_action=_SimInfoSkillFixupAction.TunableFactory(
                    description=
                    '\n                    A fix up action to set a skill with a specific level.\n                    '
                ),
                unlock_fixup_action=_SimInfoUnlockFixupAction.TunableFactory(
                    description=
                    '\n                    A fix up action to unlock certain things for a Sim\n                    '
                ),
                perk_fixup_action=_SimInfoPerkFixupAction.TunableFactory(
                    description=
                    '\n                    A fix up action to grant perks to a Sim. It checks perk required\n                    unlock tuning and unlocks prerequisite perks first.\n                    '
                ),
                default='career_fixup_action'),
            tuning_group=GroupNames.CAS),
        'sim_info_fixup_actions_timing':
        TunableEnumEntry(
            description=
            "\n            This is DEPRECATED, don't tune this field. We usually don't do trait-based\n            fixup unless it's related to CAS stories. We keep this field only for legacy\n            support reason.\n            \n            This is mostly to optimize performance when applying fix-ups to\n            a Sim.  We ideally would not like to spend time scanning every Sim \n            on every load to see if they need fixups.  Please be sure you \n            consult a GPE whenever you are creating fixup tuning.\n            ",
            tunable_type=SimInfoFixupActionTiming,
            default=SimInfoFixupActionTiming.ON_FIRST_SIMINFO_LOAD,
            tuning_group=GroupNames.DEPRECATED,
            deprecated=True),
        'teleport_style_interaction_to_inject':
        TunableReference(
            description=
            '\n             When this trait is added to a Sim, if a teleport style interaction\n             is specified, any time another interaction runs, we may run this\n             teleport style interaction to shorten or replace the route to the \n             target.\n             ',
            manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION),
            class_restrictions=('TeleportStyleSuperInteraction', ),
            allow_none=True,
            tuning_group=GroupNames.SPECIAL_CASES),
        'interactions':
        OptionalTunable(
            description=
            '\n            Mixer interactions that are available to Sims equipped with this\n            trait.\n            ',
            tunable=ContentSet.TunableFactory(locked_args={
                'phase_affordances': frozendict(),
                'phase_tuning': None
            })),
        'buffs_add_on_spawn_only':
        Tunable(
            description=
            '\n            If unchecked, buffs are added to the Sim as soon as this trait is\n            added. If checked, buffs will be added only when the Sim is\n            instantiated and removed when the Sim uninstantiates.\n            \n            General guidelines: If the buffs only matter to Sims, for example\n            buffs that alter autonomy behavior or walkstyle, this should be\n            checked.\n            ',
            tunable_type=bool,
            default=True),
        'buffs':
        TunableList(
            description=
            '\n            Buffs that should be added to the Sim whenever this trait is\n            equipped.\n            ',
            tunable=TunableBuffReference(pack_safe=True),
            unique_entries=True),
        'buffs_proximity':
        TunableList(
            description=
            '\n            Proximity buffs that are active when this trait is equipped.\n            ',
            tunable=TunableReference(manager=services.buff_manager())),
        'buff_replacements':
        TunableMapping(
            description=
            '\n            A mapping of buff replacement. If Sim has this trait on, whenever he\n            get the buff tuned in the key of the mapping, it will get replaced\n            by the value of the mapping.\n            ',
            key_type=TunableReference(
                description=
                '\n                Buff that will get replaced to apply on Sim by this trait.\n                ',
                manager=services.buff_manager(),
                reload_dependent=True,
                pack_safe=True),
            value_type=TunableTuple(
                description=
                '\n                Data specific to this buff replacement.\n                ',
                buff_type=TunableReference(
                    description=
                    '\n                    Buff used to replace the buff tuned as key.\n                    ',
                    manager=services.buff_manager(),
                    reload_dependent=True,
                    pack_safe=True),
                buff_reason=OptionalTunable(
                    description=
                    '\n                    If enabled, override the buff reason.\n                    ',
                    tunable=TunableLocalizedString(
                        description=
                        '\n                        The overridden buff reason.\n                        '
                    )),
                buff_replacement_priority=TunableEnumEntry(
                    description=
                    "\n                    The priority of this buff replacement, relative to other\n                    replacements. Tune this to be a higher value if you want\n                    this replacement to take precedence.\n                    \n                    e.g.\n                     (NORMAL) trait_HatesChildren (buff_FirstTrimester -> \n                                                   buff_FirstTrimester_HatesChildren)\n                     (HIGH)   trait_Male (buff_FirstTrimester -> \n                                          buff_FirstTrimester_Male)\n                                          \n                     In this case, both traits have overrides on the pregnancy\n                     buffs. However, we don't want males impregnated by aliens\n                     that happen to hate children to lose their alien-specific\n                     buffs. Therefore we tune the male replacement at a higher\n                     priority.\n                    ",
                    tunable_type=TraitBuffReplacementPriority,
                    default=TraitBuffReplacementPriority.NORMAL))),
        'excluded_mood_types':
        TunableList(
            TunableReference(
                description=
                '\n            List of moods that are prevented by having this trait.\n            ',
                manager=services.mood_manager())),
        'outfit_replacements':
        TunableMapping(
            description=
            "\n            A mapping of outfit replacements. If the Sim has this trait, outfit\n            change requests are intercepted to produce the tuned result. If\n            multiple traits with outfit replacements exist, the behavior is\n            undefined.\n            \n            Tuning 'Invalid' as a key acts as a fallback and applies to all\n            reasons.\n            \n            Tuning 'Invalid' as a value keeps a Sim in their current outfit.\n            ",
            key_type=TunableEnumEntry(tunable_type=OutfitChangeReason,
                                      default=OutfitChangeReason.Invalid),
            value_type=TunableEnumEntry(tunable_type=OutfitChangeReason,
                                        default=OutfitChangeReason.Invalid)),
        'disable_aging':
        OptionalTunable(
            description=
            '\n            If enabled, aging out of specific ages can be disabled.\n            ',
            tunable=TunableTuple(
                description=
                '\n                The tuning that disables aging out of specific age groups.\n                ',
                allowed_ages=TunableSet(
                    description=
                    '\n                    A list of ages that the Sim CAN age out of. If an age is in\n                    this list then the Sim is allowed to age out of it. If an\n                    age is not in this list than a Sim is not allowed to age out\n                    of it. For example, if the list only contains Child and\n                    Teen, then a Child Sim would be able to age up to Teen and\n                    a Teen Sim would be able to age up to Young Adult. But, a\n                    Young Adult, Adult, or Elder Sim would not be able to age\n                    up.\n                    ',
                    tunable=TunableEnumEntry(Age, default=Age.ADULT)),
                tooltip=OptionalTunable(
                    description=
                    '\n                    When enabled, this tooltip will be displayed in the aging\n                    progress bar when aging is disabled because of the trait.\n                    ',
                    tunable=TunableLocalizedStringFactory(
                        description=
                        '\n                        The string that displays in the aging UI when aging up\n                        is disabled due to the trait.\n                        '
                    ))),
            tuning_group=GroupNames.SPECIAL_CASES),
        'can_die':
        Tunable(
            description=
            '\n            When set, Sims with this trait are allowed to die. When unset, Sims\n            are prevented from dying.\n            ',
            tunable_type=bool,
            default=True,
            tuning_group=GroupNames.SPECIAL_CASES),
        'culling_behavior':
        TunableVariant(
            description=
            '\n            The culling behavior of a Sim with this trait.\n            ',
            default_behavior=CullingBehaviorDefault.TunableFactory(),
            immune_to_culling=CullingBehaviorImmune.TunableFactory(),
            importance_as_npc_score=CullingBehaviorImportanceAsNpc.
            TunableFactory(),
            default='default_behavior',
            tuning_group=GroupNames.SPECIAL_CASES),
        'always_send_test_event_on_add':
        Tunable(
            description=
            '\n            If checked, will send out a test event when added to a trait\n            tracker even if the receiving sim is hidden or not instanced.\n            ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES),
        'voice_effect':
        OptionalTunable(
            description=
            '\n            The voice effect of a Sim with this trait. This is prioritized\n            against other traits with voice effects.\n            \n            The Sim may only have one voice effect at a time.\n            ',
            tunable=VoiceEffectRequest.TunableFactory()),
        'plumbbob_override':
        OptionalTunable(
            description=
            '\n            If enabled, allows a new plumbbob model to be used when a Sim has\n            this occult type.\n            ',
            tunable=PlumbbobOverrideRequest.TunableFactory()),
        'vfx_mask':
        OptionalTunable(
            description=
            '\n            If enabled when this trait is added the masks will be applied to\n            the Sim affecting the visibility of specific VFX.\n            Example: TRAIT_CHILDREN will provide a mask MASK_CHILDREN which \n            the monster battle object will only display VFX for any Sim \n            using that mask.\n            ',
            tunable=TunableEnumFlags(
                description=
                "\n                Mask that will be added to the Sim's mask when the trait is\n                added.\n                ",
                enum_type=VFXMask),
            enabled_name='apply_vfx_mask',
            disabled_name='no_vfx_mask'),
        'day_night_tracking':
        OptionalTunable(
            description=
            "\n            If enabled, allows this trait to track various aspects of day and\n            night via buffs on the owning Sim.\n            \n            For example, if this is enabled and the Sunlight Buff is tuned with\n            buffs, the Sim will get the buffs added every time they're in\n            sunlight and removed when they're no longer in sunlight.\n            ",
            tunable=DayNightTracking.TunableFactory()),
        'persistable':
        Tunable(
            description=
            '\n            If checked then this trait will be saved onto the sim.  If\n            unchecked then the trait will not be saved.\n            Example unchecking:\n            Traits that are applied for the sim being in the region.\n            ',
            tunable_type=bool,
            default=True),
        'initial_commodities':
        TunableSet(
            description=
            '\n            A list of commodities that will be added to a sim on load, if the\n            sim has this trait.\n            \n            If a given commodity is also blacklisted by another trait that the\n            sim also has, it will NOT be added.\n            \n            Example:\n            Adult Age Trait adds Hunger.\n            Vampire Trait blacklists Hunger.\n            Hunger will not be added.\n            ',
            tunable=Commodity.TunableReference(pack_safe=True)),
        'initial_commodities_blacklist':
        TunableSet(
            description=
            "\n            A list of commodities that will be prevented from being\n            added to a sim that has this trait.\n            \n            This always takes priority over any commodities listed in any\n            trait's initial_commodities.\n            \n            Example:\n            Adult Age Trait adds Hunger.\n            Vampire Trait blacklists Hunger.\n            Hunger will not be added.\n            ",
            tunable=Commodity.TunableReference(pack_safe=True)),
        'ui_commodity_sort_override':
        OptionalTunable(
            description=
            '\n            Optional list of commodities to override the default UI sort order.\n            ',
            tunable=TunableList(
                description=
                '\n                The position of the commodity in this list represents the sort order.\n                Add all possible combination of traits in the list.\n                If we have two traits which have sort override, we will implement\n                a priority system to determine which determines which trait sort\n                order to use.\n                ',
                tunable=Commodity.TunableReference())),
        'ui_category':
        OptionalTunable(
            description=
            '\n            If enabled then this trait will be displayed in a specific category\n            within the relationship panel if this trait would be displayed\n            within that panel.\n            ',
            tunable=TunableEnumEntry(
                description=
                '\n                The UI trait category that we use to categorize this trait\n                within the relationship panel.\n                ',
                tunable_type=TraitUICategory,
                default=TraitUICategory.PERSONALITY),
            export_modes=ExportModes.All,
            enabled_name='ui_trait_category_tag'),
        'loot_on_trait_add':
        OptionalTunable(
            description=
            '\n            If tuned, this list of loots will be applied when trait is added in game.\n            ',
            tunable=TunableList(
                description=
                '\n                List of loot to apply on the sim when this trait is added not\n                through CAS.\n                ',
                tunable=TunableReference(
                    description=
                    '\n                    Loot to apply.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.ACTION),
                    pack_safe=True))),
        'npc_leave_lot_interactions':
        OptionalTunable(
            description=
            '\n            If enabled, allows tuning a set of Leave Lot and Leave Lot Must Run\n            interactions that this trait provides. NPC Sims with this trait will\n            use these interactions to leave the lot instead of the defaults.\n            ',
            tunable=TunableTuple(
                description=
                '\n                Leave Lot Now and Leave Lot Now Must Run interactions.\n                ',
                leave_lot_now_interactions=TunableSet(
                    TunableReference(
                        description=
                        '\n                    If tuned, the Sim will consider these interaction when trying to run\n                    any "leave lot" situation.\n                    ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.INTERACTION),
                        allow_none=False,
                        pack_safe=True)),
                leave_lot_now_must_run_interactions=TunableSet(
                    TunableReference(
                        description=
                        '\n                    If tuned, the Sim will consider these interaction when trying to run\n                    any "leave lot must run" situation.\n                    ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.INTERACTION),
                        allow_none=False,
                        pack_safe=True)))),
        'hide_relationships':
        Tunable(
            description=
            '\n            If checked, then any relationships with a Sim who has this trait\n            will not be displayed in the UI. This is done by keeping the\n            relationship from having any tracks to actually track which keeps\n            it out of the UI.\n            ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.RELATIONSHIP),
        'whim_set':
        OptionalTunable(
            description=
            '\n            If enabled then this trait will offer a whim set to the Sim when it\n            is active.\n            ',
            tunable=TunableReference(
                description=
                '\n                A whim set that is active when this trait is active.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ASPIRATION),
                class_restrictions=('ObjectivelessWhimSet', ))),
        'allow_from_gallery':
        Tunable(
            description=
            '\n            If checked, then this trait is allowed to be transferred over from\n            Sims downloaded from the gallery.\n            ',
            tunable_type=bool,
            default=True,
            tuning_group=GroupNames.SPECIAL_CASES),
        'remove_on_death':
        Tunable(
            description=
            '\n            If checked, when a Sim dies this trait will be removed.\n            ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES),
        'build_buy_purchase_tracking':
        OptionalTunable(
            description=
            '\n            If enabled, allows this trait to track various build-buy purchases\n            via event listening in the trait tracker.\n            ',
            tunable=TunableList(
                description=
                '\n                Loots to apply to the hamper when clothing pile is being put.\n                ',
                tunable=TunableReference(manager=services.get_instance_manager(
                    sims4.resources.Types.ACTION),
                                         class_restrictions=('LootActions', ),
                                         pack_safe=True)))
    }
    _asm_param_name = None
    default_trait_params = set()

    def __repr__(self):
        return '<Trait:({})>'.format(self.__name__)

    def __str__(self):
        return '{}'.format(self.__name__)

    @classmethod
    def _tuning_loaded_callback(cls):
        cls._asm_param_name = cls.trait_asm_overrides.trait_asm_param
        if cls._asm_param_name is None:
            cls._asm_param_name = cls.__name__
        if cls.trait_asm_overrides.trait_asm_param is not None and cls.trait_asm_overrides.consider_for_boundary_conditions:
            cls.default_trait_params.add(
                cls.trait_asm_overrides.trait_asm_param)
        for (buff, replacement_buff) in cls.buff_replacements.items():
            if buff.trait_replacement_buffs is None:
                buff.trait_replacement_buffs = {}
            buff.trait_replacement_buffs[cls] = replacement_buff
        for mood in cls.excluded_mood_types:
            if mood.excluding_traits is None:
                mood.excluding_traits = []
            mood.excluding_traits.append(cls)

    @classmethod
    def _verify_tuning_callback(cls):
        if cls.display_name:
            if not cls.display_name_gender_neutral.hash:
                logger.error(
                    'Trait {} specifies a display name. It must also specify a gender-neutral display name. These must use different string keys.',
                    cls,
                    owner='BadTuning')
            if cls.display_name._string_id == cls.display_name_gender_neutral.hash:
                logger.error(
                    'Trait {} has the same string tuned for its display name and its gender-neutral display name. These must be different strings for localization.',
                    cls,
                    owner='BadTuning')
        if cls.day_night_tracking is not None:
            if not cls.day_night_tracking.sunlight_buffs and not (
                    not cls.day_night_tracking.shade_buffs
                    and not (not cls.day_night_tracking.day_buffs
                             and not cls.day_night_tracking.night_buffs)):
                logger.error(
                    'Trait {} has Day Night Tracking enabled but no buffs are tuned. Either tune buffs or disable the tracking.',
                    cls,
                    owner='BadTuning')
            else:
                tracking_buff_tag = Trait.DAY_NIGHT_TRACKING_BUFF_TAG
                if any(
                        buff for buff in cls.day_night_tracking.sunlight_buffs
                        if not buff.buff_type.has_tag(tracking_buff_tag)
                ) or (any(buff for buff in cls.day_night_tracking.shade_buffs
                          if not buff.buff_type.has_tag(tracking_buff_tag))
                      or any(buff for buff in cls.day_night_tracking.day_buffs
                             if not buff.buff_type.has_tag(tracking_buff_tag))
                      ) or any(
                          buff
                          for buff in cls.day_night_tracking.night_buffs
                          if not buff.buff_type.has_tag(tracking_buff_tag)):
                    logger.error(
                        'Trait {} has Day Night tracking with an invalid\n                    buff. All buffs must be tagged with {} in order to be\n                    used as part of Day Night Tracking. Add these buffs with the\n                    understanding that, regardless of what system added them, they\n                    will always be on the Sim when the condition is met (i.e.\n                    Sunlight Buffs always added with sunlight is out) and they will\n                    always be removed when the condition is not met. Even if another\n                    system adds the buff, they will be removed if this trait is\n                    tuned to do that.\n                    ',
                        cls, tracking_buff_tag)
        for buff_reference in cls.buffs:
            if buff_reference.buff_type.broadcaster is not None:
                logger.error(
                    'Trait {} has a buff {} with a broadcaster tuned that will never be removed. This is a potential performance hit, and a GPE should decide whether this is the best place for such.',
                    cls,
                    buff_reference,
                    owner='rmccord')
        for commodity in cls.initial_commodities:
            if not commodity.persisted_tuning:
                logger.error(
                    'Trait {} has an initial commodity {} that does not have persisted tuning.',
                    cls, commodity)

    @classproperty
    def is_personality_trait(cls):
        return cls.trait_type == TraitType.PERSONALITY

    @classproperty
    def is_aspiration_trait(cls):
        return cls.trait_type == TraitType.ASPIRATION

    @classproperty
    def is_gender_option_trait(cls):
        return cls.trait_type == TraitType.GENDER_OPTIONS

    @classproperty
    def is_ghost_trait(cls):
        return cls.trait_type == TraitType.GHOST

    @classproperty
    def is_robot_trait(cls):
        return cls.trait_type == TraitType.ROBOT

    @classmethod
    def is_valid_trait(cls, sim_info_data):
        if cls.ages and sim_info_data.age not in cls.ages:
            return False
        if cls.genders and sim_info_data.gender not in cls.genders:
            return False
        elif cls.species and sim_info_data.species not in cls.species:
            return False
        return True

    @classmethod
    def should_apply_fixup_actions(cls, fixup_source):
        if cls.sim_info_fixup_actions and cls.sim_info_fixup_actions_timing == fixup_source:
            if fixup_source != SimInfoFixupActionTiming.ON_FIRST_SIMINFO_LOAD:
                logger.warn(
                    'Trait {} has fixup actions not from CAS flow.This should only happen to old saves before EP08',
                    cls,
                    owner='yozhang')
            return True
        return False

    @classmethod
    def apply_fixup_actions(cls, sim_info):
        for fixup_action in cls.sim_info_fixup_actions:
            fixup_action(sim_info)

    @classmethod
    def can_age_up(cls, current_age):
        if not cls.disable_aging:
            return True
        return current_age in cls.disable_aging.allowed_ages

    @classmethod
    def is_conflicting(cls, trait):
        if trait is None:
            return False
        if cls.conflicting_traits and trait in cls.conflicting_traits:
            return True
        elif trait.conflicting_traits and cls in trait.conflicting_traits:
            return True
        return False

    @classmethod
    def get_outfit_change_reason(cls, outfit_change_reason):
        replaced_reason = cls.outfit_replacements.get(
            outfit_change_reason if outfit_change_reason is not None else
            OutfitChangeReason.Invalid)
        if replaced_reason is not None:
            return replaced_reason
        elif outfit_change_reason is not None:
            replaced_reason = cls.outfit_replacements.get(
                OutfitChangeReason.Invalid)
            if replaced_reason is not None:
                return replaced_reason
        return outfit_change_reason

    @classmethod
    def get_teleport_style_interaction_to_inject(cls):
        return cls.teleport_style_interaction_to_inject
class EnvironmentScoreComponent(
        Component,
        component_name=objects.components.types.ENVIRONMENT_SCORE_COMPONENT,
        allow_dynamic=True):
    __qualname__ = 'EnvironmentScoreComponent'
    ENVIRONMENT_SCORE_ZERO = (frozendict(), 0, 0, ())

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._broadcaster = None
        definition = self.owner.definition
        self._environment_scores = {}
        self._negative_environment_score = definition.negative_environment_score
        self._positive_environment_score = definition.positive_environment_score
        for (index, tag) in enumerate(definition.environment_score_mood_tags):
            mood = EnvironmentScoreTuning.ENVIRONMENT_SCORE_MOODS.get(tag)
            while mood is not None:
                score = definition.environment_scores[index]
                if score is not None:
                    self._environment_scores[mood] = score
        self._has_static_scoring = None
        self._state_environment_scores = []

    def _start_broadcaster(self):
        if not self.should_broadcast:
            return
        broadcaster_service = services.current_zone().broadcaster_service
        if broadcaster_service is not None and self._broadcaster is None:
            self._broadcaster = EnvironmentScoreTuning.ENVIRONMENT_SCORE_BROADCASTER(
                broadcasting_object=self.owner)
            broadcaster_service.add_broadcaster(self._broadcaster)

    def _stop_broadcaster(self):
        if self._broadcaster is not None:
            broadcaster_service = services.current_zone().broadcaster_service
            if broadcaster_service is not None:
                broadcaster_service.remove_broadcaster(self._broadcaster)
            self._broadcaster = None

    def on_add(self, *_, **__):
        self._start_broadcaster()

    def on_remove(self, *_, **__):
        self._stop_broadcaster()

    def on_added_to_inventory(self):
        self._stop_broadcaster()

    def on_removed_from_inventory(self):
        self._start_broadcaster()

    @property
    def has_static_scoring(self):
        if self._has_static_scoring is None:
            self._has_static_scoring = self.owner.environment_score_trait_modifiers or (
                self._environment_scores or
                (self._negative_environment_score != 0
                 or self._positive_environment_score != 0))
        return self._has_static_scoring

    @property
    def should_broadcast(self):
        if not self.has_static_scoring and not self.is_mood_scoring_enabled(
        ) and len(self._state_environment_scores) <= 1:
            return False
        return True

    @componentmethod_with_fallback(lambda mood: 0)
    def get_base_environment_score(self, mood):
        return self._environment_scores.get(mood, 0)

    def add_state_environment_score(self, environment_score_state):
        self._state_environment_scores.append(environment_score_state)
        if environment_score_state.state_value is not EnvironmentScoreTuning.DISABLED_STATE_VALUE:
            self._start_broadcaster()

    def remove_state_environment_score(self, environment_score_state):
        if environment_score_state in self._state_environment_scores:
            self._state_environment_scores.remove(environment_score_state)
        if not self.should_broadcast:
            self._stop_broadcaster()

    def is_mood_scoring_enabled(self):
        for state in self._state_environment_scores:
            while state.state_value is EnvironmentScoreTuning.DISABLED_STATE_VALUE:
                return False
        return True

    @objects.components.componentmethod_with_fallback(lambda _: [])
    def potential_component_interactions(self, context, **kwargs):
        affordance = None
        (mood_scores, _, _,
         _) = self._compute_environment_score(sim=None,
                                              ignore_disabled_state=True)
        has_mood_scoring = mood_scores is not None and sum(
            mood_scores.values()) > 0
        if not has_mood_scoring:
            return
        state_values = [
            state.state_value for state in self._state_environment_scores
        ]
        if EnvironmentScoreTuning.ENABLED_STATE_VALUE in state_values:
            affordance = EnvironmentScoreTuning.DISABLE_AFFORDANCE
        elif EnvironmentScoreTuning.DISABLED_STATE_VALUE in state_values:
            affordance = EnvironmentScoreTuning.ENABLE_AFFORDANCE
        if affordance is not None:
            yield interactions.aop.AffordanceObjectPair(
                affordance, self.owner, affordance, None, **kwargs)

    @componentmethod_with_fallback(
        lambda sim: EnvironmentScoreComponent.ENVIRONMENT_SCORE_ZERO)
    def get_environment_score(self, sim=None, ignore_disabled_state=False):
        (mood_scores, negative_score, positive_score,
         contributions) = self._compute_environment_score(
             sim, ignore_disabled_state=ignore_disabled_state)
        if not mood_scores and negative_score == 0 and positive_score == 0:
            (mood_scores, negative_score, positive_score,
             contributions) = self.ENVIRONMENT_SCORE_ZERO
        return (mood_scores, negative_score, positive_score, contributions)

    def _compute_environment_score(self,
                                   sim=None,
                                   ignore_disabled_state=False):
        object_mood_modifiers = {}
        negative_modifiers = (0, 1)
        positive_modifiers = (0, 1)
        contributions = []
        gsi_enabled = sim is not None and gsi_handlers.sim_handlers_log.environment_score_archiver.enabled
        if sim is not None:
            trait_tracker = sim.sim_info.trait_tracker
        else:
            trait_tracker = None
        if trait_tracker is not None:
            for (trait, trait_modifiers
                 ) in self.owner.environment_score_trait_modifiers.items():
                while trait in trait_tracker.equipped_traits:
                    (object_mood_modifiers, negative_modifiers,
                     positive_modifiers) = trait_modifiers.combine_modifiers(
                         object_mood_modifiers, negative_modifiers,
                         positive_modifiers)
                    if gsi_enabled:
                        contributions.extend(
                            gsi_handlers.sim_handlers_log.
                            get_environment_score_object_contributions(
                                self.owner, 'Trait: {} on Object:{}'.format(
                                    trait.__name__,
                                    gsi_handlers.gsi_utils.format_object_name(
                                        self.owner)), trait_modifiers))
        for state in self._state_environment_scores:
            while not (ignore_disabled_state and state.state_value is
                       EnvironmentScoreTuning.DISABLED_STATE_VALUE):
                if state.state_value is EnvironmentScoreTuning.ENABLED_STATE_VALUE:
                    pass
                value_base_modifiers = state.base_modifiers
                (object_mood_modifiers, negative_modifiers,
                 positive_modifiers) = value_base_modifiers.combine_modifiers(
                     object_mood_modifiers, negative_modifiers,
                     positive_modifiers)
                if gsi_enabled:
                    contributions.extend(
                        gsi_handlers.sim_handlers_log.
                        get_environment_score_object_contributions(
                            self.owner,
                            'State Value: ' + state.state_value.__name__,
                            value_base_modifiers))
                while trait_tracker is not None:
                    while True:
                        for (trait, state_trait_modifiers
                             ) in state.trait_modifiers.items():
                            while trait in trait_tracker.equipped_traits:
                                (object_mood_modifiers, negative_modifiers,
                                 positive_modifiers
                                 ) = state_trait_modifiers.combine_modifiers(
                                     object_mood_modifiers, negative_modifiers,
                                     positive_modifiers)
                                if gsi_enabled:
                                    contributions.extend(
                                        gsi_handlers.sim_handlers_log.
                                        get_environment_score_object_contributions(
                                            self.owner,
                                            'Trait: {} in State Value: {}'.
                                            format(trait.__name__,
                                                   state.state_value.__name__),
                                            state_trait_modifiers))
        mood_scores = {}
        instance_manager = services.get_instance_manager(
            sims4.resources.Types.MOOD)
        for mood in instance_manager.types.values():
            if ignore_disabled_state or self.is_mood_scoring_enabled():
                mood_score = self._environment_scores.get(mood, 0)
                current_mood_score = mood_scores.get(mood, 0)
                if gsi_enabled and mood_score != 0:
                    contributions.append({
                        'object':
                        gsi_handlers.gsi_utils.format_object_name(self.owner),
                        'object_id':
                        self.owner.id,
                        'source':
                        'Definition : ' + self.owner.definition.name,
                        'score_affected':
                        mood.__name__,
                        'adder':
                        mood_score,
                        'multiplier':
                        1
                    })
                mood_modifiers = object_mood_modifiers.get(mood)
                if mood_modifiers is not None:
                    (adder, multiplier) = mood_modifiers
                    current_mood_score = current_mood_score + (
                        mood_score + adder) * multiplier
                else:
                    current_mood_score = current_mood_score + mood_score
            else:
                current_mood_score = 0
            mood_scores[mood] = current_mood_score
        if gsi_enabled and self._negative_environment_score != 0:
            contributions.append({
                'object':
                gsi_handlers.gsi_utils.format_object_name(self.owner),
                'object_id':
                self.owner.id,
                'source':
                'Definition : ' + self.owner.definition.name,
                'score_affected':
                'NEGATIVE SCORING',
                'adder':
                self._negative_environment_score,
                'multiplier':
                1
            })
        if gsi_enabled and self._positive_environment_score != 0:
            contributions.append({
                'object':
                gsi_handlers.gsi_utils.format_object_name(self.owner),
                'object_id':
                self.owner.id,
                'source':
                'Definition : ' + self.owner.definition.name,
                'score_affected':
                'POSITIVE SCORING',
                'adder':
                self._positive_environment_score,
                'multiplier':
                1
            })
        negative_score = (self._negative_environment_score +
                          negative_modifiers[0]) * negative_modifiers[1]
        positive_score = (self._positive_environment_score +
                          positive_modifiers[0]) * positive_modifiers[1]
        return (mood_scores, negative_score, positive_score, contributions)
Exemplo n.º 16
0
from singletons import DEFAULT, UNSET
from tag import Tag
from world.spawn_point import SpawnPointOption
import gsi_handlers
import profanity
import server
import services
import sims
import sims4.log
import sims4.math
import terrain

logger = sims4.log.Logger('Sim Spawner')
disable_spawning_non_selectable_sims = False
OUTFITS_TO_POPULATE_ON_SPAWN = frozendict({
    Species.HUMAN: (OutfitCategory.SWIMWEAR, OutfitCategory.HOTWEATHER,
                    OutfitCategory.COLDWEATHER)
})


class SimCreator:
    def __init__(self,
                 gender=None,
                 age=None,
                 species=None,
                 first_name='',
                 last_name='',
                 breed_name='',
                 first_name_key=0,
                 last_name_key=0,
                 full_name_key=0,
                 breed_name_key=0,
Exemplo n.º 17
0
from _sims4_collections import frozendict
from protocolbuffers import DistributorOps_pb2, UI_pb2, WeatherSeasons_pb2
from distributor.ops import Op
from distributor.rollback import ProtocolBufferRollback
from seasons.seasons_enums import SeasonType
import date_and_time
START_SEASON_VALUES = frozendict({
    SeasonType.SUMMER: 3.5,
    SeasonType.FALL: 0.5,
    SeasonType.WINTER: 1.5,
    SeasonType.SPRING: 2.5
})
MAX_SEASON_INTERPOLATE_VALUE = 4.0


class SeasonInterpolationOp(Op):
    def __init__(self, season_type, season_content, mid_season_op=False):
        super().__init__()
        self.op = WeatherSeasons_pb2.SeasonWeatherInterpolations()
        with ProtocolBufferRollback(
                self.op.season_weather_interlops) as season_interlop:
            season_interlop.message_type = WeatherSeasons_pb2.SeasonWeatherInterpolationMessage.SEASON
            if mid_season_op:
                season_interlop.start_value = season_type.value
                season_interlop.end_value = season_type.value + 1
                start_time = season_content.midpoint_time
                season_interlop.start_time = int(
                    start_time /
                    date_and_time.REAL_MILLISECONDS_PER_SIM_SECOND)
                season_interlop.end_time = int(
                    (start_time + season_content.length) /
Exemplo n.º 18
0
	def __init__ (self, description = 'A single tunable test.', test_excluded = (), test_locked_args = { }, **kwargs):
		kwargs.update((test_name, TunableTestVariant.cached_tunable_test(test_factory, frozendict(test_locked_args))) for (test_name, test_factory) in TunableTestVariant.TEST_VARIANTS.items() if test_name not in test_excluded)
		super().__init__(description = description, **kwargs)
Exemplo n.º 19
0
 def __init__(self, overrides=None, params=frozendict(), vfx=frozendict(), sounds=frozendict(), props=frozendict(), prop_state_values=frozendict(), manifests=frozendict(), required_slots=None, balloons=None, reactionlet=None, animation_context=None, alternative_props=None):
     if overrides is None:
         self.params = frozendict(params)
         self.vfx = frozendict(vfx)
         self.sounds = frozendict(sounds)
         self.props = frozendict(props)
         self.prop_state_values = frozendict(prop_state_values)
         self.manifests = frozendict(manifests)
         self.required_slots = required_slots or ()
         self.balloons = balloons or ()
         self.reactionlet = reactionlet or None
         self.animation_context = animation_context or None
         self.balloon_target_override = None
         self.alternative_props = alternative_props or {}
     else:
         self.params = frozendict(params, overrides.params)
         self.vfx = frozendict(vfx, overrides.vfx)
         self.sounds = frozendict(sounds, overrides.sounds)
         self.props = frozendict(props, overrides.props)
         self.prop_state_values = frozendict(prop_state_values, overrides.prop_state_values)
         self.manifests = frozendict(manifests, overrides.manifests)
         self.required_slots = overrides.required_slots or (required_slots or ())
         self.balloons = overrides.balloons or (balloons or ())
         self.reactionlet = overrides.reactionlet or (reactionlet or None)
         self.animation_context = overrides.animation_context or (animation_context or None)
         self.balloon_target_override = overrides.balloon_target_override or None
         self.alternative_props = overrides.alternative_props or {}
Exemplo n.º 20
0
 def _define_supported_postures(cls):
     supported_postures = super()._define_supported_postures()
     if supported_postures:
         return supported_postures
     return frozendict({ParticipantType.Actor: cls.POSTURE_MANIFEST})
Exemplo n.º 21
0
 def create_situation(self,
                      situation_type,
                      guest_list=None,
                      user_facing=True,
                      duration_override=None,
                      custom_init_writer=None,
                      zone_id=0,
                      scoring_enabled=True,
                      spawn_sims_during_zone_spin_up=False,
                      creation_source=None,
                      travel_request_kwargs=frozendict(),
                      linked_sim_id=GLOBAL_SITUATION_LINKED_SIM_ID,
                      scheduled_time=None,
                      **extra_kwargs):
     zone = services.current_zone()
     if zone.is_zone_shutting_down:
         return
     current_zone_id = services.current_zone_id()
     situation_type = services.narrative_service(
     ).get_possible_replacement_situation(situation_type)
     if services.get_zone_modifier_service().is_situation_prohibited(
             zone_id if zone_id else current_zone_id, situation_type):
         return
     if guest_list is None:
         guest_list = SituationGuestList()
     hire_cost = guest_list.get_hire_cost()
     host_sim_info = guest_list.host_sim_info
     if host_sim_info is not None and not host_sim_info.household.funds.try_remove(
             situation_type.cost() + hire_cost,
             Consts_pb2.TELEMETRY_EVENT_COST, host_sim_info):
         return
     situation_id = id_generator.generate_object_id()
     self._send_create_situation_telemetry(situation_type, situation_id,
                                           guest_list, hire_cost, zone_id)
     if zone_id and zone_id != current_zone_id and scheduled_time is None:
         return self._create_situation_and_travel(
             situation_type,
             situation_id,
             guest_list,
             user_facing,
             duration_override,
             custom_init_writer,
             zone_id,
             scoring_enabled=scoring_enabled,
             creation_source=creation_source,
             linked_sim_id=linked_sim_id,
             travel_request_kwargs=travel_request_kwargs)
     situation_seed = SituationSeed(
         situation_type,
         SeedPurpose.NORMAL,
         situation_id,
         guest_list,
         user_facing=user_facing,
         duration_override=duration_override,
         zone_id=zone_id,
         scoring_enabled=scoring_enabled,
         spawn_sims_during_zone_spin_up=spawn_sims_during_zone_spin_up,
         creation_source=creation_source,
         linked_sim_id=linked_sim_id,
         **extra_kwargs)
     if custom_init_writer is not None:
         situation_seed.setup_for_custom_init_params(custom_init_writer)
     return_id = None
     if scheduled_time is not None:
         uid = services.drama_scheduler_service().schedule_node(
             self.DEFAULT_PLAYER_PLANNED_DRAMA_NODE,
             SingleSimResolver(guest_list.host_sim.sim_info),
             specific_time=scheduled_time,
             situation_seed=situation_seed)
         return_id = situation_id if uid is not None else None
     else:
         return_id = self.create_situation_from_seed(situation_seed)
     return return_id