class CustomAnimation(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {'get': TunableAnimationReference(callback=None), 'put': TunableAnimationReference(callback=None)}

    def get_access_animation_factory(self, is_put):
        if is_put:
            return self.put
        else:
            return self.get

    def get_access_constraint(self, is_put, inventory_owner):
        animation_factory = self.get_access_animation_factory(is_put)
        if animation_factory is None:
            return
        constraint = animation_factory().get_constraint()
        return constraint
Exemple #2
0
 def __init__(
         self,
         locked_animation_args=DEFAULT,
         animation_callback=DEFAULT,
         interaction_asm_type=DEFAULT,
         override_animation_context=False,
         participant_enum_override=DEFAULT,
         description='There is an arbitrary number of possible animation selectors, based on participation in group.',
         **kwargs):
     if participant_enum_override is DEFAULT:
         participant_enum_type = ParticipantTypeAnimation
         participant_enum_default = ParticipantTypeAnimation.Invalid
     else:
         participant_enum_type = participant_enum_override[0]
         participant_enum_default = participant_enum_override[1]
     super().__init__(groups=TunableList(
         TunableTuple(
             group=TunableEnumFlags(
                 participant_enum_type,
                 participant_enum_default,
                 description='The group the Sim must be a participant of'),
             animation_ref=TunableAnimationReference(
                 allow_reactionlets=False,
                 override_animation_context=override_animation_context,
                 callback=animation_callback)),
         description='A list of difficulty to animation mappings.'),
                      description=description,
                      **kwargs)
class RouteFailureTunables:
    __qualname__ = 'RouteFailureTunables'
    route_fail_animation = TunableAnimationReference(description='\n                               Route Failure Animation                     \n                               Note: Route Failure Balloons are handled specially and not tuned here. See: route_fail_overrides_object, route_fail_overrides_build\n                               ', callback=None)
    route_fail_overrides_object = TunableAnimationOverrides()
    route_fail_overrides_reservation = TunableAnimationOverrides()
    route_fail_overrides_build = TunableAnimationOverrides()
    route_fail_overrides_no_dest_node = TunableAnimationOverrides()
    route_fail_overrides_no_path_found = TunableAnimationOverrides()
    route_fail_overrides_no_valid_intersection = TunableAnimationOverrides()
    route_fail_overrides_no_goals_generated = TunableAnimationOverrides()
    route_fail_overrides_no_connectivity = TunableAnimationOverrides()
    route_fail_overrides_path_plan_fail = TunableAnimationOverrides()
 def __init__(
         self,
         description="If the target object isn't in the given state, use an asm and set that object's state.",
         **kwargs):
     super().__init__(
         value=TunableStateValueReference(
             description='The value to require'),
         xevt_id=Tunable(
             int,
             None,
             description=
             "An xevt on which to change the state's value (optional)."),
         animation_ref=TunableAnimationReference(),
         description=description,
         **kwargs)
Exemple #5
0
 def __init__(self, animation_callback=DEFAULT, allow_multi_si_cancel=False, allow_social_animation=False, locked_args=None, **kwargs):
     import interactions.base.basic
     animation_ref = TunableAnimationReference(callback=animation_callback, interaction_asm_type=InteractionAsmType.Outcome, description='The one-shot animation ref to play')
     animation_ref = OptionalTunable(animation_ref)
     if allow_multi_si_cancel:
         cancel_si = OptionalTunable(TunableEnumFlags(description='\n                                                                     Every participant in this list will have the SI\n                                                                     associated with the interaction owning this outcome\n                                                                     canceled.\n                                                                     ', enum_type=ParticipantType))
     else:
         cancel_si = TunableVariant(description="\n                                        When enabled, the outcome will cancel the owning\n                                        interaction's parent SI.\n                                        ", locked_args={'enabled': ParticipantType.Actor, 'disabled': None}, default='disabled')
     if allow_social_animation:
         kwargs['social_animation'] = OptionalTunable(description='\n                If enabled, specify an animation that will be played once for\n                the entire social group.\n                ', tunable=TunableAnimationReference())
     else:
         locked_args = {} if locked_args is None else locked_args
         locked_args['social_animation'] = None
     if locked_args is None:
         locked_args = {TunableOutcomeActions.ADD_TARGET_AFFORDANCE_LOOT_KEY: True}
     elif TunableOutcomeActions.ADD_TARGET_AFFORDANCE_LOOT_KEY not in locked_args:
         locked_args.update({TunableOutcomeActions.ADD_TARGET_AFFORDANCE_LOOT_KEY: True})
     super().__init__(animation_ref=animation_ref, xevt=OptionalTunable(description='\n                             When specified, the outcome will be associated to this xevent.\n                             ', tunable=Tunable(tunable_type=int, default=None)), response=OptionalTunable(TunableResponseSelector()), force_outcome_on_exit=Tunable(description='\n                             If checked outcome will always be given even if\n                             interaction was canceled. If unchecked, outcome\n                             will only be given if mixer, one_shot, or\n                             naturally finishing interaction.\n                             ', tunable_type=bool, default=False), loot_list=TunableList(description='\n                             A list of pre-defined loot operations.\n                             ', tunable=LootActions.TunableReference()), consume_object=Tunable(description="\n                             If checked, the loot list generated in the target\n                             object's consumable component will be added to\n                             this outcome's loot list.\n                             ", tunable_type=bool, default=False), continuation=TunableContinuation(description='An affordance to be pushed as part of an outcome.'), cancel_si=cancel_si, events_to_send=TunableList(TunableEnumEntry(TestEvent, TestEvent.Invalid, description='events types to send')), display_message=OptionalTunable(description='\n                             If set, flyaway text will be shown.\n                             ', tunable=TunableLocalizedString(default=None, description='Localized string that is shown as flyaway text')), loot_offset_override=OptionalTunable(Tunable(int, 0), description="An override to interactions.utils.loot's DEFAULT_LOOT_OFFSET tuning, specifically for this outcome."), parameterized_autonomy=TunableMapping(description='\n                             Specify parameterized autonomy for the participants of the interaction.\n                             ', key_type=TunableEnumEntry(description='\n                                 The participant to run parameterized autonomy for.\n                                 ', tunable_type=ParticipantType, default=ParticipantType.Actor), value_type=TunableList(description='\n                                 A list of parameterized autonomy requests to run.\n                                 ', tunable=TunableParameterizedAutonomy())), outcome_result=TunableEnumEntry(description='\n                             The interaction outcome result to consider this interaction result. This is\n                             important for testing interactions, such as an aspiration that wants to know\n                             if a Sim has successfully kissed another Sim. All interactions that a designer\n                             would consider a success case for such a scenario would be assured here.\n                             ', tunable_type=OutcomeResult, default=OutcomeResult.SUCCESS), basic_extras=interactions.base.basic.TunableBasicExtrasCore(), locked_args=locked_args, **kwargs)
Exemple #6
0
 def __init__(
         self,
         locked_animation_args=DEFAULT,
         animation_callback=DEFAULT,
         interaction_asm_type=DEFAULT,
         override_animation_context=False,
         participant_enum_override=DEFAULT,
         description='An animation to play based on the behavior of selector.',
         **kwargs):
     super().__init__(selector=TunableVariant(
         single_ref=TunableAnimationReference(
             allow_reactionlets=False,
             interaction_asm_type=interaction_asm_type,
             override_animation_context=override_animation_context,
             callback=animation_callback),
         participant=TunableAnimationSelectorParticipant(
             locked_animation_args=locked_animation_args,
             animation_callback=animation_callback,
             interaction_asm_type=interaction_asm_type,
             override_animation_context=override_animation_context,
             participant_enum_override=participant_enum_override),
         default='single_ref'),
                      description=description,
                      **kwargs)
class ShooTunables:
    __qualname__ = 'ShooTunables'
    shoo_animation = TunableAnimationReference(description='Shoo Animation', callback=None)
 def __init__(self, *args, **kwargs):
     super().__init__(
         *args,
         _asm_key=TunableResourceKey(
             description=
             '\n                The posture ASM.\n                ',
             default=None,
             resource_types=[sims4.resources.Types.STATEMACHINE],
             category='asm',
             pack_safe=True),
         _actor_param_name=Tunable(
             description=
             "\n                The name of the actor parameter in this posture's ASM. By\n                default, this is x, and you should probably not change it.\n                ",
             tunable_type=str,
             default='x',
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMActorSim),
         _target_name=Tunable(
             description=
             "\n                The actor name for the target object of this posture. Leave\n                empty for postures with no target. In the case of a posture\n                that targets an object, it should be the name of the object\n                actor in this posture's ASM.\n                ",
             tunable_type=str,
             default=None,
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMActorAll),
         _jig_name=Tunable(
             description=
             '\n                The actor name for the jig created by this posture, if a jig is\n                tuned.\n                ',
             tunable_type=str,
             default=None,
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMActorObject),
         _enter_state_name=Tunable(
             description=
             '\n                The name of the entry state for the posture in the ASM. All\n                postures should have two public states, not including entry\n                and exit. This should be the first of the two states.\n                ',
             tunable_type=str,
             default=None,
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMState),
         _exit_state_name=Tunable(
             description=
             '\n                The name of the exit state in the ASM. By default, this is\n                exit.\n                ',
             tunable_type=str,
             default='exit',
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMState),
         _state_name=Tunable(
             description=
             '\n                The main state name for the looping posture pose in the\n                ASM. All postures should have two public states, not\n                including entry and exit. This should be the second of the\n                two states.\n                ',
             tunable_type=str,
             default=None,
             source_location=self.ASM_SOURCE,
             source_query=SourceQueries.ASMState),
         _idle_animation=TunableAnimationReference(
             description=
             '\n                The animation for a Sim to play while in this posture and\n                waiting for interaction behavior to start.\n                ',
             callback=None,
             pack_safe=True),
         _idle_animation_occult_overrides=TunableMapping(
             description=
             '\n                A mapping of occult type to idle animation override data.\n                ',
             key_type=TunableEnumEntry(
                 description=
                 '\n                    The occult type of the Sim.\n                    ',
                 tunable_type=OccultType,
                 default=OccultType.HUMAN),
             value_type=TunableAnimationReference(
                 description=
                 '\n                    Idle animation overrides to use for a Sim based on their \n                    occult type.\n                    ',
                 callback=None,
                 pack_safe=True)),
         _set_locomotion_surface=Tunable(
             description=
             '\n                If checked, then the Sim\'s locomotion surface is set to the\n                target of this posture, if it exists.\n                \n                The locomotion surface affects the sound of the Sim\'s footsteps\n                when locomoting. Generally, this should be unset, since most\n                Sims don\'t route on objects as part of postures. For the cases\n                where they do, however, we need to ensure the sound is properly\n                overridden.\n                \n                e.g. The "Sit" posture for Cats includes sitting on objects.\n                Some of those transitions involve Cats walking across the sofa.\n                We need to ensure that the sound of the footsteps matches the\n                fabric, instead of the floor/ground.\n                ',
             tunable_type=bool,
             default=False),
         _carry_constraint=OptionalTunable(
             description=
             '\n                If enabled, Sims in this posture need to be picked up using this\n                specific constraint.\n                ',
             tunable=TunableList(tunable=TunableConstraintVariant(
                 description=
                 '\n                        A constraint that must be fulfilled in order to pick up\n                        this Sim.\n                        '
             )),
             enabled_name='Override',
             disabled_name='From_Carryable_Component'),
         **kwargs)
Exemple #9
0
	def animation_callback (callback = DEFAULT):
		return {
			'animation_ref': TunableAnimationReference(description = ' \n                A looping animation reference.\n                ', callback = callback, allow_none = True, reload_dependent = True)
		}
Exemple #10
0
 def _on_tunable_loaded_callback(cls, fields, source, *, animations):
     for (participant_type, animation_element) in animations.items():
         add_auto_constraints = TunableAnimationReference.get_default_callback(InteractionAsmType.Response)
         add_auto_constraints(cls, fields, source, actor_participant_type=participant_type, target_participant_type=None, factory=animation_element.factory, overrides=animation_element.overrides)
Exemple #11
0
class WatchSuperInteraction(SuperInteraction):
    INSTANCE_TUNABLES = {
        'off_channel':
        TunableStateValueReference(
            description=
            '\n            The off channel. The last Sim using the TV will set the object to\n            this state.\n            '
        ),
        'required_channel':
        TunableStateValueReference(
            description='\n            The channel to watch.\n            '),
        'remote_animation':
        TunableAnimationReference(
            description=
            '\n            The animation for using the TV remote.\n            '
        ),
        'sim_view_discourage_area_width':
        Tunable(
            description=
            '\n            The width of the discouragement region placed from a viewing Sim to\n            the TV.\n            ',
            tunable_type=float,
            default=0.4)
    }
    CHANGE_CHANNEL_XEVT_ID = 101

    def _add_route_goal_suppression_region_to_quadtree(self, *args, **kwargs):
        if self.target is None:
            return
        object_point = self.target.location.transform.translation
        sim_point = self.sim.intended_location.transform.translation
        delta = object_point - sim_point
        delta_length = delta.magnitude()
        sim_point_offset = self.sim_view_discourage_area_width * 2
        if delta_length < sim_point_offset:
            return
        start_point = sim_point + delta / (delta_length / sim_point_offset)
        geo = build_rectangle_from_two_points_and_radius(
            object_point, start_point, self.sim_view_discourage_area_width)
        services.sim_quadtree().insert(self.sim, self.id,
                                       placement.ItemType.ROUTE_GOAL_PENALIZER,
                                       geo, self.sim.routing_surface, False, 0)

    def _remove_route_goal_suppression_region_from_quadtree(self):
        services.sim_quadtree().remove(self.id,
                                       placement.ItemType.ROUTE_GOAL_PENALIZER,
                                       0)

    def _refresh_watching_discouragement_stand_region(self, *args, **kwargs):
        self._remove_route_goal_suppression_region_from_quadtree()
        self._add_route_goal_suppression_region_to_quadtree()

    def _start_route_goal_suppression(self, _):
        self.sim.routing_component.on_intended_location_changed.append(
            self._refresh_watching_discouragement_stand_region)
        self._add_route_goal_suppression_region_to_quadtree()

    def _stop_route_goal_suppression(self, _):
        self._remove_route_goal_suppression_region_from_quadtree()
        self.sim.routing_component.on_intended_location_changed.remove(
            self._refresh_watching_discouragement_stand_region)

    def ensure_state(self, desired_channel):
        return conditional_animation(self, desired_channel,
                                     self.CHANGE_CHANNEL_XEVT_ID,
                                     self.affordance.remote_animation)

    def _changed_state_callback(self, target, state, old_value, new_value):
        if new_value is not self.off_channel and new_value.affordance is not None:
            context = self.context.clone_for_continuation(self)
            affordance = self.generate_continuation_affordance(
                new_value.affordance)
            aop = AffordanceObjectPair(affordance, self.target, affordance,
                                       None)
            aop.test_and_execute(context)
        self.cancel(
            FinishingType.OBJECT_CHANGED,
            cancel_reason_msg=
            'state: interaction canceled on state change ({} != {})'.format(
                new_value.value, self.required_channel.value))

    def _run_interaction_gen(self, timeline):
        result = yield from element_utils.run_child(
            timeline,
            build_critical_section_with_finally(
                self._start_route_goal_suppression,
                build_critical_section(
                    build_critical_section(
                        self.ensure_state(self.affordance.required_channel),
                        objects.components.state.with_on_state_changed(
                            self.target,
                            self.affordance.required_channel.state,
                            self._changed_state_callback,
                            super()._run_interaction_gen)),
                    maybe(
                        lambda: len(self.target.get_users(sims_only=True)) ==
                        1, self.ensure_state(self.off_channel))),
                self._stop_route_goal_suppression))
        return result
        yield
class GetPutComponentMixin(HasTunableFactory, metaclass=ComponentMetaclass):
    __qualname__ = 'GetPutComponentMixin'
    GENERIC_GET_ANIMATION = TunableAnimationReference()
    GENERIC_PUT_ANIMATION = TunableAnimationReference()
    GENERIC_CONSTRAINT = Anywhere()

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

    @classmethod
    def add_auto_constraint(cls, participant_type, tuned_constraint, **kwargs):
        cls.GENERIC_CONSTRAINT = cls.GENERIC_CONSTRAINT.intersect(
            tuned_constraint)

    FACTORY_TUNABLES = {
        'get_put':
        TunableVariant(
            description=
            '\n                This controls the behavior of a Sim who wants to get from or\n                put to the component owner.\n                ',
            default='none',
            locked_args={'none': None},
            generic=GenericAnimation.TunableFactory(),
            custom=CustomAnimation.TunableFactory())
    }

    def __init__(self, *args, get_put=None, **kwargs):
        super().__init__(*args, **kwargs)
        self._get_put = get_put

    def _get_access_constraint(self, sim, is_put, carry_target, resolver=None):
        if self._get_put is None:
            return
        constraint = self._get_put.get_access_constraint(is_put, self.owner)

        def constraint_resolver(animation_participant, default=None):
            if resolver is not None:
                result = resolver(animation_participant, default=default)
                if result is not default:
                    return result
            if animation_participant == AnimationParticipant.ACTOR:
                return sim
            if animation_participant in (AnimationParticipant.CARRY_TARGET,
                                         AnimationParticipant.TARGET):
                return carry_target
            if animation_participant == AnimationParticipant.SURFACE:
                return self.owner
            return default

        concrete_constraint = constraint.apply_posture_state(
            None, constraint_resolver)
        return concrete_constraint

    def get_surface_target(self, sim):
        inv_owner = self.owner
        body_target = sim.posture.target
        if body_target is not None and body_target.inventory_component is not None and body_target.inventory_component.inventory_type == inv_owner.inventory_component.inventory_type:
            return body_target
        return inv_owner

    def _get_access_animation(self, is_put):
        if self._get_put is None:
            return
        animation_factory = self._get_put.get_access_animation_factory(is_put)
        if animation_factory is None:
            return

        def append_animations(arb, sim, carry_target, carry_track,
                              animation_context, surface_height):
            asm = sim.posture.get_asm(animation_context,
                                      animation_factory.asm_key,
                                      None,
                                      use_cache=False)
            asm.set_parameter('surfaceHeight', surface_height)
            sim.posture.setup_asm_interaction(
                asm,
                sim,
                None,
                animation_factory.actor_name,
                None,
                carry_target=carry_target,
                carry_target_name=animation_factory.carry_target_name,
                surface_target=self.get_surface_target(sim),
                carry_track=carry_track)
            asm.set_actor_parameter(animation_factory.carry_target_name,
                                    carry_target, PARAM_CARRY_TRACK,
                                    carry_track.name.lower())
            animation_factory.append_to_arb(asm, arb)
            animation_factory.append_exit_to_arb(asm, arb)

        return append_animations
Exemple #13
0
 def animation_callback(callback=DEFAULT):
     return {'animation_ref': TunableAnimationReference(description=' \n                A non-looping animation reference.\n                ', callback=callback), 'periodic_stat_change': OptionalTunable(description='\n                Statistic changes tuned to occur every specified interval.\n                ', tunable=PeriodicStatisticChangeElement.TunableFactory())}
Exemple #14
0
class WatchSuperInteraction(interactions.base.super_interaction.SuperInteraction):
    __qualname__ = 'WatchSuperInteraction'
    INSTANCE_TUNABLES = {'required_channel': objects.components.state.TunableStateValueReference(description='The channel that this affordance watches.'), 'remote_animation': TunableAnimationReference(description='The animation for using the TV remote.'), 'sim_view_discourage_area_width': Tunable(float, 0.4, description='The width of the discouragement region placed from a viewing Sim to the TV.')}
    CHANGE_CHANNEL_XEVT_ID = 101

    @classmethod
    def _verify_tuning_callback(cls):
        super()._verify_tuning_callback()
        if cls.required_channel is None:
            logger.error('Tuning: {} is missing a Required Channel.', cls.__name__)
        if cls.remote_animation is None:
            logger.error('Tuning: {} is missing a Remote Animation.', cls.__name__)

    def _add_route_goal_suppression_region_to_quadtree(self, *args, **kwargs):
        object_point = self.target.location.transform.translation
        sim_point = self.sim.intended_location.transform.translation
        geo = build_rectangle_from_two_points_and_radius(object_point, sim_point, self.sim_view_discourage_area_width)
        services.sim_quadtree().insert(self.sim, self.id, placement.ItemType.ROUTE_GOAL_PENALIZER, geo, self.sim.routing_surface.secondary_id, False, 0)

    def _remove_route_goal_suppression_region_from_quadtree(self):
        services.sim_quadtree().remove(self.id, placement.ItemType.ROUTE_GOAL_PENALIZER, 0)

    def _refresh_watching_discouragement_stand_region(self, *args, **kwargs):
        self._remove_route_goal_suppression_region_from_quadtree()
        self._add_route_goal_suppression_region_to_quadtree()

    def _start_route_goal_suppression(self, _):
        self.sim.on_intended_location_changed.append(self._refresh_watching_discouragement_stand_region)
        self._add_route_goal_suppression_region_to_quadtree()

    def _stop_route_goal_suppression(self, _):
        self._remove_route_goal_suppression_region_from_quadtree()
        self.sim.on_intended_location_changed.remove(self._refresh_watching_discouragement_stand_region)

    def ensure_state(self, desired_channel):
        return conditional_animation(self, desired_channel, self.CHANGE_CHANNEL_XEVT_ID, self.affordance.remote_animation)

    def _changed_state_callback(self, target, state, old_value, new_value):
        if new_value != Television.TV_OFF_STATE:
            context = self.context.clone_for_continuation(self)
            affordance = self.generate_continuation_affordance(new_value.affordance)
            aop = AffordanceObjectPair(affordance, self.target, affordance, None)
            aop.test_and_execute(context)
        self.cancel(FinishingType.OBJECT_CHANGED, cancel_reason_msg='state: interaction canceled on state change ({} != {})'.format(new_value.value, self.required_channel.value))

    def _run_interaction_gen(self, timeline):
        result = yield element_utils.run_child(timeline, build_critical_section_with_finally(self._start_route_goal_suppression, build_critical_section(build_critical_section(self.ensure_state(self.affordance.required_channel), objects.components.state.with_on_state_changed(self.target, self.affordance.required_channel.state, self._changed_state_callback, super()._run_interaction_gen)), maybe(lambda : len(self.target.get_users(sims_only=True)) == 1, self.ensure_state(Television.TV_OFF_STATE))), self._stop_route_goal_suppression))
        return result
Exemple #15
0
 def __init__(self, description='A response that plays distinct animations on Actor and TargetSim.', **kwargs):
     super().__init__(callback=self._on_tunable_loaded_callback, animations=TunableMapping(key_type=TunableEnumEntry(ParticipantTypeResponse, ParticipantType.Actor), key_name='target', value_type=TunableAnimationReference(callback=None, interaction_asm_type=InteractionAsmType.Response, participant_enum_override=(ParticipantTypeResponse, ParticipantTypeResponse.Invalid)), value_name='animation', description='A mapping of participants to the animation they should play. If a Sim is found more than onceit will only have the first animation played on it. Tuning this field will override animation_actorand animation_target.'), description=description, **kwargs)
Exemple #16
0
 def animation_callback(callback=DEFAULT):
     return {'animation_ref': OptionalTunable(TunableAnimationReference(description=' \n                A non-looping animation reference.\n                ', callback=callback, reload_dependent=True))}
Exemple #17
0
class ListenSuperInteraction(SuperInteraction):
    __qualname__ = 'ListenSuperInteraction'
    INSTANCE_TUNABLES = {'required_station': objects.components.state.TunableStateValueReference(description='\n            The station that this affordance listens to.\n            '), 'remote_animation': TunableAnimationReference(description='\n            The animation for using the stereo remote.\n            '), 'off_state': objects.components.state.TunableStateValueReference(description='\n            The channel that represents the off state.\n            ')}
    CHANGE_CHANNEL_XEVT_ID = 101

    @classmethod
    def _verify_tuning_callback(cls):
        super()._verify_tuning_callback()
        if cls.required_station is None:
            logger.error('Tuning: {} is missing a Required Channel.', cls.__name__)
        if cls.remote_animation is None:
            logger.error('Tuning: {} is missing a Remote Animation.', cls.__name__)

    def ensure_state(self, desired_station):
        return conditional_animation(self, desired_station, self.CHANGE_CHANNEL_XEVT_ID, self.affordance.remote_animation)

    def _changed_state_callback(self, target, state, old_value, new_value):
        if new_value != self.off_state:
            object_callback = getattr(new_value, 'on_interaction_canceled_from_state_change', None)
            if object_callback is not None:
                object_callback(self)
        self.cancel(FinishingType.OBJECT_CHANGED, cancel_reason_msg='state: interaction canceled on state change ({} != {})'.format(new_value.value, self.required_station.value))

    def _run_interaction_gen(self, timeline):
        result = yield element_utils.run_child(timeline, build_critical_section(self.ensure_state(self.affordance.required_station), objects.components.state.with_on_state_changed(self.target, self.affordance.required_station.state, self._changed_state_callback, super()._run_interaction_gen)))
        return result
class Posture(metaclass=TunedInstanceMetaclass, manager=services.posture_manager()):
    __qualname__ = 'Posture'
    ASM_SOURCE = '_asm_key'
    INSTANCE_TUNABLES = {'mobile': Tunable(bool, False, tuning_filter=FilterTag.EXPERT_MODE, description='If True, the Sim can route in this posture.'), 'unconstrained': Tunable(bool, False, description='If True, the Sim can stand anywhere in this posture.'), 'ownable': Tunable(bool, True, description="If True, This posture is ownable by interactions. Ex: A posture like carry_nothing should not be ownable, because it will cause strange cancelations that don't make sense."), 'social_geometry': TunableTuple(social_space=TunablePolygon(description="\n             The special geometry override for socialization in this posture. This defines\n             where the Sim's attention is focused and informs the social positioning system where\n             each Sim should stand to look most natural when interacting with this Sim. \n             Ex: we override the social geometry for a Sim who is bartending to be a wider cone \n             and be in front of the bar instead of embedded within the bar. This encourages Sims \n             to stand on the customer-side of the bar to socialize with this Sim instead of coming \n             around the back."), focal_point=TunableVector3(sims4.math.Vector3.ZERO(), description='Focal point when socializing in this posture, relative to Sim'), tuning_filter=FilterTag.EXPERT_MODE, description='The special geometry for socialization in this posture.'), ASM_SOURCE: TunableResourceKey(None, [sims4.resources.Types.STATEMACHINE], tuning_group=GroupNames.ANIMATION, description='The posture ASM.', category='asm'), '_actor_param_name': Tunable(str, 'x', source_location=ASM_SOURCE, source_query=SourceQueries.ASMActorSim, tuning_group=GroupNames.ANIMATION, description="\n             The name of the actor parameter in this posture's ASM. By default, this is x, and you should probably\n             not change it."), '_target_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMActorAll, tuning_group=GroupNames.ANIMATION, description="\n             The actor name for the target object of this posture. Leave empty for postures with no target. \n             In the case of a posture that targets an object, it should be the name of the object actor in \n             this posture's ASM. \n             ."), '_enter_state_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n             The name of the entry state for the posture in the ASM. \n             All postures should have two public states, not including entry and exit.\n             This should be the first of the two states.'), '_exit_state_name': Tunable(str, 'exit', source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n             The name of the exit state in the ASM. By default, this is exit.'), '_state_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n             The main state name for the looping posture pose in the ASM.\n             All postures should have two public states, not including entry and exit.\n             This should be the second of the two states.'), '_supported_postures': TunableList(TunableTuple(posture_type=TunableReference(services.posture_manager(), description='A supported posture.'), entry=Tunable(bool, True, description=''), exit=Tunable(bool, True, description=''), transition_cost=OptionalTunable(Tunable(float, 1, description="Cost of the transition to this posture then calculating the Sim's transition sequence.")), preconditions=TunableEnumFlags(PosturePreconditions, PosturePreconditions.NONE), description='A list of postures that this posture supports entrance from and exit to. Defaults to [stand]')), '_supports_carry': Tunable(description='\n            Whether or not there should be a carry version of this posture in\n            the posture graph.\n            ', tunable_type=bool, default=True), 'censor_level': TunableEnumEntry(CensorState, None, tuning_filter=FilterTag.EXPERT_MODE, description="\n                                                                                The type of censor grid that will be applied to any Sim in this posture.  \n                                                                                A censor grid obscures different parts of a Sim's body depending on what censor level it is set at.  \n                                                                                For example, the LHAND censor level will obscure a Sim's left hand.  \n                                                                                By default, postures have no censor level association, which means no censor grid will be applied to them \n                                                                                and every part of their body will be visible when in this posture.\n                                                                                "), 'outfit_change': TunableOutfitChange(description='\n            Define what outfits the Sim is supposed to wear when entering or\n            exiting this posture.\n            '), 'cost': Tunable(float, 0, description='( >= 0 ) The distance a sim is willing to pay to avoid using this posture (higher number discourage using the posture)'), 'idle_animation': TunableAnimationReference(callback=None, tuning_group=GroupNames.ANIMATION, description='The animation for a Sim to play while in this posture and waiting for interaction behavior to start.'), 'jig': OptionalTunable(TunableReference(manager=services.definition_manager(), description='The jig to place while the Sim is in this posture.'), description='An optional Jig to place while the Sim is in this posture.'), 'allow_affinity': Tunable(bool, True, description="\n                            If True, Sims will prefer to use this posture if someone\n                            they're interacting with is using the posture.\n                            \n                            Ex: If you chat with a sitting sim, you will prefer to\n                            sit with them and chat.\n                            "), 'additional_put_down_distance': Tunable(description="\n            An additional distance in front of the Sim to start searching for\n            valid put down locations when in this posture.\n            \n            This tunable is only respected for the Sim's body posture.\n            ", tunable_type=float, default=0.5), 'additional_interaction_jig_fgl_distance': Tunable(description='\n            An additional distance (in meters) in front of the Sim to start \n            searching when using FGL to place a Jig to run an interaction.', tunable_type=float, default=0)}
    DEFAULT_POSTURE = TunableReference(services.get_instance_manager(sims4.resources.Types.POSTURE), description="The default affordance to use as the supported posture if nothing is tuned in a Posture's 'Supported Postures'")
    IS_BODY_POSTURE = True

    def test(self):
        return True

    @classproperty
    def target_name(cls):
        return cls._target_name

    def __init__(self, sim, target, track, animation_context=None):
        self._create_asm(animation_context=animation_context)
        self._source_interaction = None
        self._primitive = None
        self._owning_interactions = set()
        self._sim = None
        self._target = None
        self._target_part = None
        self._surface_target_ref = None
        self._track = None
        self._slot_constraint = UNSET
        self._context = None
        self._asm_registry = defaultdict(dict)
        self._asms_with_posture_info = set()
        self._failed_parts = set()
        self._bind(sim, target, track)
        self._linked_posture = None
        self._entry_anim_complete = False
        self._exit_anim_complete = False
        self.external_transition = False
        self._active_cancel_aops = WeakSet()
        self._saved_exit_clothing_change = None

    @classproperty
    def name(cls):
        return cls._posture_name or cls.__name__

    @property
    def posture_context(self):
        return self._context

    @property
    def animation_context(self):
        return self._animation_context

    @property
    def surface_target(self):
        return self.sim.posture_state.surface_target

    @property
    def source_interaction(self):
        return self._source_interaction

    @source_interaction.setter
    def source_interaction(self, value):
        if value is None:
            logger.error('Posture {} get a None source interaction set', self)
            return
        self._source_interaction = value

    @property
    def owning_interactions(self):
        return self._owning_interactions

    def last_owning_interaction(self, interaction):
        if interaction not in self.owning_interactions:
            return False
        for owning_interaction in self.owning_interactions:
            while owning_interaction is not interaction and not owning_interaction.is_finishing:
                return False
        return True

    def add_owning_interaction(self, interaction):
        self._owning_interactions.add(interaction)

    def remove_owning_interaction(self, interaction):
        self._owning_interactions.remove(interaction)

    def clear_owning_interactions(self):
        from interactions.base.interaction import OWNS_POSTURE_LIABILITY
        try:
            for interaction in list(self._owning_interactions):
                interaction.remove_liability((OWNS_POSTURE_LIABILITY, self.track))
        finally:
            self._owning_interactions.clear()

    def add_cancel_aop(self, cancel_aop):
        self._active_cancel_aops.add(cancel_aop)

    def kill_cancel_aops(self):
        for interaction in self._active_cancel_aops:
            interaction.cancel(FinishingType.INTERACTION_QUEUE, cancel_reason_msg='PostureOwnership. This posture wasgoing to be canceled, but another interaction took ownership over the posture. Most likely the current posture was already valid for the new interaction.')

    def get_idle_behavior(self):
        if self.idle_animation is None:
            logger.error('{} has no idle animation tuning! This tuning is required for all body postures!', self)
            return
        if self.source_interaction is None:
            logger.error('Posture({}) on sim:{} has no source interaction.', self, self.sim, owner='Maxr', trigger_breakpoint=True)
            return
        if self.owning_interactions and not self.multi_sim:
            interaction = list(self.owning_interactions)[0]
        else:
            interaction = self.source_interaction
        idle = self.idle_animation(interaction)
        auto_exit = get_auto_exit((self.sim,), asm=idle.get_asm())
        return build_critical_section(auto_exit, idle, flush_all_animations)

    def log_info(self, phase, msg=None):
        from sims.sim_log import log_posture
        log_posture(phase, self, msg=msg)

    def _create_asm(self, animation_context=None):
        self._animation_context = animation_context or AnimationContext()
        self._animation_context.add_posture_owner(self)
        self._asm = animation.asm.Asm(self._asm_key, self._animation_context)

    _provided_postures = PostureManifest().intern()
    _posture_name = None
    family_name = None

    @classproperty
    def posture_type(cls):
        return cls

    @classmethod
    def is_same_posture_or_family(cls, other_cls):
        if cls == other_cls:
            return True
        return cls.family_name is not None and cls.family_name == other_cls.family_name

    @classmethod
    def _tuning_loading_callback(cls):

        def delclassattr(name):
            if name in cls.__dict__:
                delattr(cls, name)

        delclassattr('_provided_postures')
        delclassattr('_posture_name')
        delclassattr('family_name')

    PostureTransitionData = namedtuple('PostureTransitionData', ('preconditions', 'transition_cost'))
    _posture_transitions = {}

    @staticmethod
    def _add_posture_transition(source_posture, dest_posture, transition_data):
        Posture._posture_transitions[(source_posture, dest_posture)] = transition_data

    @contextmanager
    def __reload_context__(oldobj, newobj):
        posture_transitions = dict(oldobj._posture_transitions)
        yield None
        oldobj._posture_transitions.update(posture_transitions)

    @classmethod
    def _tuning_loaded_callback(cls):
        for posture_data in cls._supported_postures:
            transition_data = cls.PostureTransitionData(posture_data.preconditions, posture_data.transition_cost)
            if posture_data.entry:
                cls._add_posture_transition(posture_data.posture_type, cls, transition_data)
            while posture_data.exit:
                cls._add_posture_transition(cls, posture_data.posture_type, transition_data)
        asm = animation.asm.Asm(cls._asm_key, get_throwaway_animation_context())
        provided_postures = asm.provided_postures
        if not provided_postures:
            return
        specific_name = None
        family_name = None
        for entry in provided_postures:
            entry_specific_name = entry.specific
            if not entry_specific_name:
                raise ValueError('{} must provide a specific posture for all posture definition rows.'.format(asm.name))
            if specific_name is None:
                specific_name = entry_specific_name
            elif entry_specific_name != specific_name:
                raise ValueError('{}: {} provides multiple specific postures: {}'.format(cls, asm.name, [specific_name, entry_specific_name]))
            entry_family_name = entry.family
            while entry_family_name:
                if family_name is None:
                    family_name = entry_family_name
                elif entry_family_name != family_name:
                    raise ValueError('{}: {} provides multiple family postures: {}'.format(cls, asm.name, [family_name, entry_family_name]))
        cls._provided_postures = provided_postures
        cls._posture_name = specific_name
        cls.family_name = family_name
        if cls.idle_animation is None:
            logger.error('{} has no idle_animation tuned. Every posture must have an idle animation suite!', cls)

    @flexmethod
    def get_provided_postures(cls, inst, surface_target=DEFAULT, concrete=False):
        if inst is None:
            return cls._provided_postures
        provided_postures = inst._provided_postures
        surface_target = inst._resolve_surface_target(surface_target)
        if surface_target is None or surface_target == MATCH_NONE:
            surface_restriction = MATCH_NONE
        elif surface_target == MATCH_ANY:
            surface_restriction = surface_target
        else:
            surface_restriction = surface_target if concrete else AnimationParticipant.SURFACE
        if surface_restriction is not None:
            filter_entry = PostureManifestEntry(MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, surface_restriction, True)
            provided_postures = provided_postures.intersection_single(filter_entry)
        return provided_postures

    def _resolve_surface_target(self, surface_target):
        if surface_target is DEFAULT:
            return self.surface_target
        return surface_target

    def _bind(self, sim, target, track):
        if self.sim is sim and self.target is target and self.target_part is None or self.target_part is target and self._track == track:
            return
        if self.target is not None and track == PostureTrack.BODY:
            part_suffix = self.get_part_suffix()
            for asm in self._asms_with_posture_info:
                while not asm.remove_virtual_actor(self._target_name, self.target, suffix=part_suffix):
                    logger.error('Failed to remove previously-bound virtual posture container {} from asm {} on posture {}.', self.target, asm, self)
        if sim is not None:
            self._sim = sim.ref()
        else:
            self._sim = None
        self._intersection = None
        self._asm_registry.clear()
        self._asms_with_posture_info.clear()
        if target is not None:
            if self._target_name is not None and target is not sim:
                (route_type, _) = target.route_target
                if self._target is not None and (self._target() is not None and self._target().parts is not None) and target in self._target().parts:
                    self._target_part = target.ref()
                else:
                    self._target_part = None
                    self._target = target.ref()
            else:
                self._target = target.ref()
        else:
            self._target_part = None
            self._target = None
        if track is not None:
            self._track = track
        else:
            self._track = None
        self._slot_constraint = UNSET

    def rebind(self, target, animation_context=None):
        self._release_animation_context()
        self._create_asm(animation_context=animation_context)
        self._bind(self.sim, target, self.track)

    def reset(self):
        if self._saved_exit_clothing_change is not None:
            self.sim.sim_info.set_current_outfit(self._saved_exit_clothing_change)
            self._saved_exit_clothing_change = None
        self._entry_anim_complete = False
        self._exit_anim_complete = False
        self._release_animation_context()
        self._source_interaction = None

    def _release_animation_context(self):
        if self._animation_context is not None:
            self._animation_context.remove_posture_owner(self)
            self._animation_context = None

    def kickstart_gen(self, timeline, posture_state):
        if PostureTrack.is_carry(self.track):
            is_body = False
            self.asm.set_parameter('location', 'inventory')
        else:
            is_body = True
            self.source_interaction = self.sim.create_default_si()
        idle_arb = animation.arb.Arb()
        self.append_transition_to_arb(idle_arb, None)
        self.append_idle_to_arb(idle_arb)
        begin_element = self.get_begin(idle_arb, posture_state)
        yield element_utils.run_child(timeline, begin_element)
        if is_body:
            default_si = self.source_interaction
            yield default_si.prepare_gen(timeline)
            yield default_si.enter_si_gen(timeline)
            yield default_si.setup_gen(timeline)
            result = yield default_si.perform_gen(timeline)
            if not result:
                raise RuntimeError('Sim: {} failed to enter default si: {}'.format(self, default_si))

    def get_asm(self, animation_context, asm_key, setup_asm_func, use_cache=True, cache_key=DEFAULT, interaction=None, posture_manifest_overrides=None, **kwargs):
        dict_key = animation_context if cache_key is DEFAULT else cache_key
        if use_cache:
            asm_dict = self._asm_registry[dict_key]
            asm = asm_dict.get(asm_key)
            if asm is None:
                asm = animation.asm.Asm(asm_key, context=animation_context, posture_manifest_overrides=posture_manifest_overrides)
                if interaction is not None:
                    asm.on_state_changed_events.append(interaction.on_asm_state_changed)
                asm_dict[asm_key] = asm
        else:
            asm = animation.asm.Asm(asm_key, context=animation_context)
            if interaction is not None:
                asm.on_state_changed_events.append(interaction.on_asm_state_changed)
        if asm.current_state == 'exit':
            asm.set_current_state('entry')
        if not (setup_asm_func is not None and setup_asm_func(asm)):
            return
        return asm

    def remove_from_cache(self, cache_key):
        if cache_key in self._asm_registry:
            for asm in self._asm_registry[cache_key].values():
                del asm._on_state_changed_events[:]
            del self._asm_registry[cache_key]

    def _create_primitive(self, animate_in, dest_state):
        return PosturePrimitive(self, animate_in, dest_state, self._context)

    def _on_reset(self):
        self._primitive = None

    def __str__(self):
        return '{0}:{1}'.format(self.name, self.id)

    def __repr__(self):
        return standard_repr(self, self.id, self.target)

    @property
    def sim(self):
        if self._sim is not None:
            return self._sim()

    @property
    def target(self):
        if self._target_part is not None:
            return self._target_part()
        if self._target is not None:
            return self._target()

    @property
    def target_part(self):
        if self._target_part is not None:
            return self._target_part()

    @property
    def track(self):
        return self._track

    @property
    def is_active_carry(self):
        return PostureTrack.is_carry(self.track) and self.target is not None

    def get_slot_offset_locked_params(self, anim_overrides=None):
        locked_params = self._locked_params
        if anim_overrides is not None:
            locked_params += anim_overrides.params
        locked_params += {'transitionPosture': 'stand'}
        return locked_params

    def build_slot_constraint(self, create_posture_state_spec_fn=None):
        if self.target is not None and PostureTrack.is_body(self.track):
            return interactions.constraints.RequiredSlot.create_slot_constraint(self, create_posture_state_spec_fn=create_posture_state_spec_fn)

    @property
    def slot_constraint_simple(self):
        if self._slot_constraint is UNSET:
            self._slot_constraint = self.build_slot_constraint(create_posture_state_spec_fn=lambda *_, **__: None)
        return self._slot_constraint

    @property
    def slot_constraint(self):
        if self._slot_constraint is UNSET:
            self._slot_constraint = self.build_slot_constraint()
        return self._slot_constraint

    @classproperty
    def multi_sim(cls):
        return False

    @property
    def is_puppet(self):
        return False

    @property
    def is_mirrored(self):
        if self.target is not None and self.target.is_part:
            return self.target.is_mirrored() or False
        return False

    @property
    def linked_posture(self):
        return self._linked_posture

    @linked_posture.setter
    def linked_posture(self, posture):
        self._linked_posture = posture

    @property
    def asm(self):
        return self._asm

    @property
    def _locked_params(self):
        anim_overrides_actor = self.sim.get_anim_overrides(self._actor_param_name)
        params = anim_overrides_actor.params
        if self.target is not None:
            anim_overrides_target = self.target.get_anim_overrides(self.target_name)
            if anim_overrides_target is not None:
                params += anim_overrides_target.params
            if self.target.is_part:
                part_suffix = self.target.part_suffix
                if part_suffix is not None:
                    params += {'subroot': part_suffix}
        if self.is_mirrored is not None:
            params += {'isMirrored': self.is_mirrored}
        return params

    @property
    def locked_params(self):
        if self.slot_constraint is None or self.slot_constraint.locked_params is None:
            return self._locked_params
        return self._locked_params + self.slot_constraint.locked_params

    def _setup_asm_container_parameter(self, asm, target, actor_name, part_suffix, target_name=None):
        if asm in self._asms_with_posture_info:
            return True
        if target_name is None:
            target_name = self._target_name
        result = False
        if target is not None and target_name is not None:
            result = asm.add_potentially_virtual_actor(actor_name, self.sim, target_name, target, part_suffix, target_participant=AnimationParticipant.CONTAINER)
            if not self._setup_custom_posture_target_name(asm, target):
                logger.error('Failed to set custom posture target {}', target)
                result = False
        if result:
            self._asms_with_posture_info.add(asm)
        return result

    def _setup_custom_posture_target_name(self, asm, target):
        _custom_target_name = target.custom_posture_target_name
        if _custom_target_name in asm.actors:
            (_custom_target_actor, _) = asm.get_actor_and_suffix(_custom_target_name)
            if _custom_target_actor is None:
                return asm.set_actor(target.custom_posture_target_name, target, suffix=None, actor_participant=AnimationParticipant.CONTAINER)
        return True

    def _setup_asm_carry_parameter(self, asm, target):
        pass

    def get_part_suffix(self, target=DEFAULT):
        if target is DEFAULT:
            target = self.target
        if target is not None:
            return target.part_suffix

    def setup_asm_posture(self, asm, sim, target, locked_params=frozendict(), actor_param_name=DEFAULT):
        if actor_param_name is DEFAULT:
            actor_param_name = self._actor_param_name
        if asm is None:
            logger.error('Attempt to setup an asm whose value is None.')
            return False
        if sim is None:
            logger.error('Attempt to setup an asm {0} on a sim whose value is None.', asm)
            return False
        if not asm.set_actor(actor_param_name, sim, actor_participant=AnimationParticipant.ACTOR):
            logger.error('Failed to set actor sim: {0} on asm {1}', actor_param_name, asm)
            return False
        sim.set_mood_asm_parameter(asm, actor_param_name)
        sim.set_trait_asm_parameters(asm, actor_param_name)
        if target.is_part:
            is_mirrored = target.is_mirrored()
            if is_mirrored is not None:
                locked_params += {'isMirrored': is_mirrored}
        part_suffix = self.get_part_suffix()
        if not (target is not None and self._target_name is not None and self._setup_asm_container_parameter(asm, target, actor_param_name, part_suffix)):
            logger.error('Failed to set actor target: {0} on asm {1}', self._target_name, asm)
            return False
        if not PostureTrack.is_body(self.track):
            self._update_non_body_posture_asm()
            sim.on_posture_event.append(self._update_on_posture_event)
        if locked_params:
            virtual_actor_map = {self._target_name: self.target}
            asm.update_locked_params(locked_params, virtual_actor_map)
        self._setup_asm_carry_parameter(asm, target)
        return True

    def _update_on_posture_event(self, change, dest_state, track, old_value, new_value):
        if change == PostureEvent.POSTURE_CHANGED:
            if track != self.track:
                if new_value is not None:
                    self._update_non_body_posture_asm()
                    if new_value != self:
                        self.sim.on_posture_event.remove(self._update_on_posture_event)
            elif new_value != self:
                self.sim.on_posture_event.remove(self._update_on_posture_event)

    def _update_non_body_posture_asm(self):
        if self.sim.posture.target is not None:
            (previous_target, previous_suffix) = self.asm.get_virtual_actor_and_suffix(self._actor_param_name, self.sim.posture._target_name)
            if previous_target is not None:
                self.asm.remove_virtual_actor(self.sim.posture._target_name, previous_target, previous_suffix)
        self.sim.posture.setup_asm_interaction(self.asm, self.sim, self.target, self._actor_param_name, self._target_name)

    def _setup_asm_interaction_add_posture_info(self, asm, sim, target, actor_name, target_name, carry_target, carry_target_name, surface_target=DEFAULT, carry_track=DEFAULT):

        def set_posture_param(posture_param_str, carry_param_str, carry_actor_name, surface_actor_name):
            if not asm.set_actor_parameter(actor_name, sim, 'posture', posture_param_str):
                if not asm.set_parameter('posture', posture_param_str):
                    return False
                logger.warn('Backwards compatibility with old posture parameter required by {}', asm.name)
            if not asm.set_actor_parameter(actor_name, sim, PARAM_CARRY_STATE, carry_param_str):
                asm.set_parameter('carry', carry_param_str)
            asm.set_parameter('isMirrored', self.is_mirrored)
            if target_name == carry_actor_name and target is not None:
                set_carry_track_param_if_needed(asm, sim, target_name, target, carry_track=carry_track)
            if carry_actor_name is not None and carry_target_name == carry_actor_name and carry_target is not None:
                set_carry_track_param_if_needed(asm, sim, carry_target_name, carry_target, carry_track=carry_track)
            if surface_actor_name is not None:
                _surface_target = self._resolve_surface_target(surface_target)
                if _surface_target:
                    asm.add_potentially_virtual_actor(actor_name, sim, surface_actor_name, _surface_target, target_participant=AnimationParticipant.SURFACE)
                else:
                    return False
            return True

        def build_carry_str(carry_state):
            if carry_state[0]:
                if carry_state[1]:
                    return 'both'
                return 'left'
            if carry_state[1]:
                return 'right'
            return 'none'

        def setup_asm_container_parameter(chosen_posture_type):
            container_name = chosen_posture_type.target_name
            if not container_name:
                return True
            part_suffix = self.get_part_suffix()
            if self._setup_asm_container_parameter(asm, self.target, actor_name, part_suffix, target_name=container_name):
                return True
            return False

        carry_state = sim.posture_state.get_carry_state()
        supported_postures = asm.get_supported_postures_for_actor(actor_name)
        if supported_postures is None:
            return True
        filtered_supported_postures = self.sim.filter_supported_postures(supported_postures)
        if surface_target is DEFAULT:
            surface_target = self._resolve_surface_target(surface_target)
            if surface_target is not None:
                surface_target_provided = MATCH_ANY
            else:
                surface_target_provided = MATCH_NONE
        elif surface_target is not None:
            surface_target_provided = MATCH_ANY
        else:
            surface_target_provided = MATCH_NONE
        provided_postures = self.get_provided_postures(surface_target=surface_target_provided)
        best_supported_posture = get_best_supported_posture(provided_postures, filtered_supported_postures, carry_state)
        if best_supported_posture is None:
            logger.debug('Failed to find supported posture for actor {} on {} for posture ({}) and carry ({}).  Interaction info claims this should work.', actor_name, asm, self, carry_state)
            return False
        carry_param_str = build_carry_str(carry_state)
        carry_actor_name = best_supported_posture.carry_target
        surface_actor_name = best_supported_posture.surface_target
        if not isinstance(surface_actor_name, str):
            surface_actor_name = None
        param_str_specific = best_supported_posture.posture_param_value_specific
        if best_supported_posture.is_overlay:
            return True
        if param_str_specific and set_posture_param(param_str_specific, carry_param_str, carry_actor_name, surface_actor_name) and setup_asm_container_parameter(best_supported_posture.posture_type_specific):
            return True
        param_str_family = best_supported_posture.posture_param_value_family
        if best_supported_posture.is_overlay:
            return True
        if param_str_family and set_posture_param(param_str_family, carry_param_str, carry_actor_name, surface_actor_name) and setup_asm_container_parameter(best_supported_posture.posture_type_family):
            return True
        return False

    def setup_asm_interaction(self, asm, sim, target, actor_name, target_name, carry_target=None, carry_target_name=None, create_target_name=None, surface_target=DEFAULT, carry_track=DEFAULT, actor_participant=AnimationParticipant.ACTOR, invalid_expected=False):
        if target_name is not None and (target_name == self._target_name and (target is not None and self.target is not None)) and target.id != self.target.id:
            if not invalid_expected:
                logger.error('Animation targets a different object than its posture, but both use the same actor name for the object. This is impossible to resolve. Actor name: {}, posture target: {}, interaction target: {}', target_name, target, self.target)
            return False
        if not asm.set_actor(actor_name, sim, actor_participant=actor_participant):
            logger.error('Failed to set actor: {0} on asm {1}', actor_name, asm)
            return False
        if sim.asm_auto_exit.apply_carry_interaction_mask:
            asm._set_actor_trackmask_override(actor_name, 50000, 'Trackmask_CarryInteraction')
        if target is not None and target_name is not None:
            from sims.sim import Sim
            if isinstance(target, Sim):
                if not target.posture.setup_asm_interaction(asm, target, None, target_name, None, actor_participant=AnimationParticipant.TARGET):
                    return False
            else:
                asm.add_potentially_virtual_actor(actor_name, sim, target_name, target, target_participant=AnimationParticipant.TARGET)
                anim_overrides = target.get_anim_overrides(target_name)
                if anim_overrides is not None and anim_overrides.params:
                    virtual_actor_map = {self._target_name: self.target}
                    asm.update_locked_params(anim_overrides.params, virtual_actor_map)
            if not self._setup_custom_posture_target_name(asm, target):
                logger.error('Unable to setup custom posture target name for {} on {}', target, asm)
        _carry_target_name = carry_target_name or create_target_name
        if carry_target is not None and _carry_target_name is not None:
            asm.add_potentially_virtual_actor(actor_name, sim, _carry_target_name, carry_target, target_participant=AnimationParticipant.CARRY_TARGET)
        if not self._setup_asm_interaction_add_posture_info(asm, sim, target, actor_name, target_name, carry_target, carry_target_name, surface_target, carry_track):
            return False
        return True

    def get_begin(self, animate_in, dest_state):
        if self._primitive is not None:
            raise RuntimeError('Posture Entry({}) called multiple times without a paired exit.'.format(self))
        self._primitive = self._create_primitive(animate_in, dest_state)
        return self._primitive.next_stage()

    def begin(self, animate_in, dest_state, context):
        self._context = context

        def _do_begin(timeline):
            logger.debug('{} begin Posture: {}', self.sim, self)
            begin = self.get_begin(animate_in, dest_state)
            result = yield element_utils.run_child(timeline, begin)
            return result

        return _do_begin

    def get_end(self):
        if self._primitive is None:
            raise RuntimeError('Posture Exit({}) called multiple times without a paired entry. Sim: {}'.format(self, self.sim))
        exit_behavior = self._primitive.next_stage()
        self._primitive = None
        return exit_behavior

    def end(self):

        def _do_end(timeline):
            logger.debug('{} end Posture: {}', self.sim, self)
            end = self.get_end()
            result = yield element_utils.run_child(timeline, end)
            return result

        return _do_end

    def add_transition_extras(self, sequence):
        return sequence

    def enumerate_goal_list_ids(self, goal_list):
        raise RuntimeError('[bhill] This function is believed to be dead code and is scheduled for pruning. If this exception has been raised, the code is not dead and this exception should be removed.')
        if goal_list is not None:
            for (index, goal) in enumerate(goal_list):
                goal.tag = index

    def get_locked_params(self, source_posture):
        if source_posture is None:
            return self._locked_params
        updates = {TRANSITION_POSTURE_PARAM_NAME: source_posture.name}
        if source_posture.target is None:
            return self._locked_params + updates
        if source_posture.target.is_part and self.target is not None and self.target.is_part:
            if self.target.is_mirrored(source_posture.target):
                direction = 'fromSimLeft'
            else:
                direction = 'fromSimRight'
            updates['direction'] = direction
        return self._locked_params + updates

    def append_transition_to_arb(self, arb, source_posture, locked_params=frozendict(), **kwargs):
        if not self._entry_anim_complete:
            locked_params += self.get_locked_params(source_posture)
            if source_posture is not None:
                locked_params += {TRANSITION_POSTURE_PARAM_NAME: source_posture.name}
            if not self.setup_asm_posture(self.asm, self.sim, self.target, locked_params=locked_params):
                logger.error('Failed to setup the asm for the posture {}', self)
                return
            self._setup_asm_target_for_transition(source_posture)
            self.asm.request(self._enter_state_name, arb)
            linked_posture = self.linked_posture
            if linked_posture is not None:
                locked_params = linked_posture.get_locked_params(source_posture)
                linked_posture.setup_asm_posture(linked_posture._asm, linked_posture.sim, linked_posture.target, locked_params=locked_params)
                if not self.multi_sim:
                    linked_posture._asm.request(linked_posture._enter_state_name, arb)
            self._entry_anim_complete = True

    def append_idle_to_arb(self, arb):
        self.asm.request(self._state_name, arb)
        if self._linked_posture is not None:
            self._linked_posture.append_idle_to_arb(arb)

    def append_exit_to_arb(self, arb, dest_state, dest_posture, var_map, locked_params=frozendict()):
        if not self._exit_anim_complete:
            self._setup_asm_target_for_transition(dest_posture)
            locked_params += self.locked_params
            if dest_posture is not None:
                locked_params += {TRANSITION_POSTURE_PARAM_NAME: dest_posture.name}
            if locked_params:
                virtual_actor_map = {self._target_name: self.target}
                self.asm.update_locked_params(locked_params, virtual_actor_map)
            self.asm.request(self._exit_state_name, arb)
            self._exit_anim_complete = True

    def _setup_asm_target_for_transition(self, transition_posture):
        if transition_posture is not None and transition_posture._target_name != self._target_name and transition_posture._target_name in self.asm.actors:
            (previous_target, previous_suffix) = self.asm.get_virtual_actor_and_suffix(self._actor_param_name, transition_posture._target_name)
            if previous_target is not None:
                self.asm.remove_virtual_actor(transition_posture.target_name, previous_target, previous_suffix)
            if not transition_posture._setup_asm_container_parameter(self.asm, transition_posture.target, self._actor_param_name, transition_posture.get_part_suffix()):
                logger.error('Failed to setup target container {} on {} from transition posture {}', transition_posture._target_name, self, transition_posture)
                return False
        return True

    def post_route_clothing_change(self, interaction, do_spin=True, **kwargs):
        si_outfit_change = interaction.outfit_change
        if si_outfit_change is not None and si_outfit_change.posture_outfit_change_overrides is not None:
            overrides = si_outfit_change.posture_outfit_change_overrides.get(self.posture_type)
            if overrides is not None:
                entry_outfit = overrides.get_on_entry_outfit(interaction)
                if entry_outfit is not None:
                    return overrides.get_on_entry_change(interaction, do_spin=do_spin, **kwargs)
        if self.outfit_change is not None:
            return self.outfit_change.get_on_entry_change(interaction, do_spin=do_spin, **kwargs)

    @property
    def saved_exit_clothing_change(self):
        return self._saved_exit_clothing_change

    def transfer_exit_clothing_change(self, clothing_change):
        self._saved_exit_clothing_change = clothing_change

    def prepare_exit_clothing_change(self, interaction):
        si_outfit_change = interaction.outfit_change
        if si_outfit_change is not None and si_outfit_change.posture_outfit_change_overrides is not None:
            overrides = si_outfit_change.posture_outfit_change_overrides.get(self.posture_type)
            if overrides is not None:
                exit_outfit = overrides.get_on_exit_outfit(interaction)
                if exit_outfit is not None:
                    self._saved_exit_clothing_change = overrides.get_on_exit_outfit(interaction)
                    return
        if self.outfit_change and self._saved_exit_clothing_change is None:
            self._saved_exit_clothing_change = self.outfit_change.get_on_exit_outfit(interaction)

    def exit_clothing_change(self, interaction, *, sim=DEFAULT, do_spin=True, **kwargs):
        if self._saved_exit_clothing_change is None or interaction is None:
            return
        if sim is DEFAULT:
            sim = interaction.sim
        sim_info = sim.sim_info
        return build_critical_section(sim_info.sim_outfits.get_change_outfit_element(self._saved_exit_clothing_change, do_spin=do_spin), flush_all_animations)

    def ensure_exit_clothing_change_application(self):
        if self.sim.posture_state.body is not self and self._saved_exit_clothing_change is not None:
            self.sim.sim_info.set_current_outfit(self._saved_exit_clothing_change)
            self._saved_exit_clothing_change = None

    @classmethod
    def supports_posture_type(cls, posture_type):
        return (cls, posture_type) in cls._posture_transitions or (posture_type, cls) in cls._posture_transitions

    @classmethod
    def is_valid_transition(cls, source_posture_type, destination_posture_type, targets_match):
        transition_data = cls._posture_transitions.get((source_posture_type, destination_posture_type))
        if transition_data is None:
            return False
        if targets_match:
            return True
        preconditions = transition_data.preconditions
        if preconditions is not None and preconditions & PosturePreconditions.SAME_TARGET:
            return False
        return True

    @classmethod
    def get_transition_cost(cls, posture_type):
        transition_data = cls._posture_transitions.get((cls, posture_type))
        if transition_data is None:
            transition_data = cls._posture_transitions.get((posture_type, cls))
        if transition_data is not None:
            return transition_data.transition_cost

    @classmethod
    def is_valid_target(cls, sim, target, **kwargs):
        return True
Exemple #19
0
class ListenSuperInteraction(SuperInteraction):
    INSTANCE_TUNABLES = {
        'required_station':
        OptionalTunable(
            description=
            "\n            If enabled, specifies the radio station this affordance listens to.\n            Normally this is provided by the object's Stereo component and does \n            not need to be tuned here.\n            ",
            tunable=objects.components.state.TunableStateValueReference(
                description=
                '\n                The station that this affordance listens to.\n                '
            ),
            enabled_by_default=True),
        'off_states':
        TunableList(
            description=
            "\n            If tuned, specifies the channels that represents the off state, and\n            can cancel this interaction. Normally this is provided by the\n            object's Stereo component and does not need to be tuned here.\n            ",
            tunable=TunableStateValueReference()),
        'remote_animation':
        TunableAnimationReference(
            description=
            '\n            The animation for using the stereo remote.\n            '
        )
    }
    CHANGE_CHANNEL_XEVT_ID = 101

    def __init__(self,
                 aop,
                 context,
                 required_station=None,
                 off_state=None,
                 **kwargs):
        super().__init__(aop, context, **kwargs)
        self.required_station = self.affordance.required_station
        self.off_state = off_state
        if required_station is not None:
            self.required_station = required_station
        elif self.required_station is None:
            stereo_component = self.target.get_component(STEREO_COMPONENT)
            if stereo_component is not None:
                current_channel = self.target.get_state(
                    stereo_component.channel_state)
                if current_channel != stereo_component.off_state:
                    self.required_station = current_channel
                    if self.off_state is None:
                        self.off_state = stereo_component.off_state
        if self.required_station is None:
            logger.error(
                'Listen interaction {} does not have a required station. This must be tuned on the interaction or on the stereo component',
                self,
                owner='thomaskenney')
        if self.off_state is None and not self.off_states:
            logger.error(
                'Listen interaction {} does not have an off state. This must be tuned on the interaction or the stereo component',
                self,
                owner='thomaskenney')

    def ensure_state(self, desired_station):
        stereo_component = self.target.get_component(STEREO_COMPONENT)
        if stereo_component is None:
            logger.error(
                'object {} being used as stereo but has no stereo component',
                self.target,
                owner='thomaskenney')
            return
        audio_state_args = {
            'immediate_audio': stereo_component.immediate,
            'play_on_active_sim_only': stereo_component.play_on_active_sim_only
        }
        attr_args_dict = {'audio_state': audio_state_args}
        return conditional_animation(self,
                                     desired_station,
                                     self.CHANGE_CHANNEL_XEVT_ID,
                                     self.affordance.remote_animation,
                                     attr_args_dict=attr_args_dict)

    def _changed_state_callback(self, target, state, old_value, new_value):
        if self.off_state and new_value != self.off_state or new_value not in self.off_states:
            object_callback = getattr(
                new_value, 'on_interaction_canceled_from_state_change', None)
            if object_callback is not None:
                object_callback(self)
        self.cancel(
            FinishingType.OBJECT_CHANGED,
            cancel_reason_msg=
            'state: interaction canceled on state change ({} != {})'.format(
                new_value.value, self.required_station.value))

    def _run_interaction_gen(self, timeline):
        result = yield from element_utils.run_child(
            timeline,
            build_critical_section(
                self.ensure_state(self.required_station),
                objects.components.state.with_on_state_changed(
                    self.target, self.required_station.state,
                    self._changed_state_callback,
                    super()._run_interaction_gen)))
        return result
        yield

    @flexmethod
    def _get_name(cls,
                  inst,
                  *args,
                  required_station=None,
                  **interaction_parameters):
        station_name = None
        if inst is not None:
            station_name = inst.required_station.display_name
        elif required_station is not None:
            station_name = required_station.display_name
        elif cls.required_station is not None:
            station_name = cls.required_station.display_name
        return cls.display_name(station_name)
class TeleportTuning:
    TELEPORT_DATA_MAPPING = TunableMapping(
        description=
        '\n        A mapping from a a teleport style to the animation, xevt and vfx data\n        that the Sim will use when a teleport is triggered.\n        ',
        key_type=TunableEnumEntry(
            description='\n            Teleport style.\n            ',
            tunable_type=TeleportStyle,
            default=TeleportStyle.NONE,
            pack_safe=True,
            invalid_enums=(TeleportStyle.NONE, )),
        value_type=TunableTuple(
            description=
            '\n            Animation and vfx data data to be used when the teleport is \n            triggered.\n            ',
            animation_outcomes=TunableList(
                description=
                '\n                One of these animations will be played when the teleport\n                happens, and weights + modifiers can be used to determine\n                exactly which animation is played based on tests.\n                ',
                tunable=TunableTuple(
                    description=
                    '\n                    A pairing of animation and weights that determine which\n                    animation is played when using this teleport style.  Any\n                    tests in the multipliers will be using the context from\n                    the interaction that plays the teleportStyle.\n                    ',
                    animation=TunableAnimationReference(
                        description=
                        '\n                        Reference of the animation to be played when the teleport is\n                        triggered.\n                        ',
                        pack_safe=True,
                        callback=None),
                    weight=TunableMultiplier.TunableFactory(
                        description=
                        '\n                        A tunable list of tests and multipliers to apply to the \n                        weight of the animation that is selected for the teleport.\n                        '
                    ))),
            start_teleport_vfx_xevt=Tunable(
                description=
                '\n                Xevent when the Sim starts teleporting to play the fade out\n                VFX.\n                ',
                tunable_type=int,
                default=100),
            start_teleport_fade_sim_xevt=Tunable(
                description=
                '\n                Xevent when the sim starts teleporting to start the fading\n                of the Sim.\n                ',
                tunable_type=int,
                default=100),
            fade_out_effect=OptionalTunable(
                description=
                '\n                If enabled, play an additional VFX on the specified \n                fade_out_xevt when fading out the Sim.\n                ',
                tunable=PlayEffect.TunableFactory(
                    description=
                    '\n                    The effect to play when the Sim fades out before actual\n                    changing its position.\n                    This effect will not be parented to the Sim, but instead will\n                    play on the bone position without attachment.  This will\n                    guarantee the VFX will not become invisible as the Sim \n                    disappears.\n                    i.e. Vampire bat teleport spawns VFX on the Sims position\n                    '
                ),
                enabled_name='play_effect',
                disabled_name='no_effect'),
            tested_fade_out_effect=TunableTestedList(
                description=
                '\n                A list of possible fade out effects to play tested against\n                the Sim that is teleporting.\n                ',
                tunable_type=PlayEffect.TunableFactory(
                    description=
                    '\n                    The effect to play when the Sim fades out before actual\n                    changing its position.\n                    This effect will not be parented to the Sim, but instead will\n                    play on the bone position without attachment.  This will\n                    guarantee the VFX will not become invisible as the Sim \n                    disappears.\n                    i.e. Vampire bat teleport spawns VFX on the Sims position\n                    '
                )),
            teleport_xevt=Tunable(
                description=
                '\n                Xevent where the teleport should happen.\n                ',
                tunable_type=int,
                default=100),
            teleport_effect=OptionalTunable(
                description=
                '\n                If enabled, play an additional VFX on the specified \n                teleport_xevt when the teleport (actual movement of the \n                position of the Sim) happens.\n                ',
                tunable=PlayEffect.TunableFactory(
                    description=
                    '\n                    The effect to play when the Sim is teleported.\n                    '
                ),
                enabled_name='play_effect',
                disabled_name='no_effect'),
            teleport_min_distance=TunableDistanceSquared(
                description=
                '\n                Minimum distance between the Sim and its target to trigger\n                a teleport.  If the distance is lower than this value, the\n                Sim will run a normal route.\n                ',
                default=5.0),
            teleport_cost=OptionalTunable(
                description=
                '\n                If enabled, the teleport will have an statistic cost every\n                time its triggered. \n                ',
                tunable=TunableTuple(
                    description=
                    '\n                    Cost and statistic to charge for a teleport event.\n                    ',
                    teleport_statistic=TunableReference(
                        description=
                        '\n                        The statistic we are operating on when a teleport \n                        happens.\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.STATISTIC),
                        pack_safe=True),
                    cost=TunableRange(
                        description=
                        '\n                        On teleport, subtract the teleport_statistic by this\n                        amount. \n                        ',
                        tunable_type=int,
                        default=1,
                        minimum=0),
                    cost_is_additive=Tunable(
                        description=
                        '\n                        If checked, the cost is additive.  Rather than deducting the cost, it will be added to\n                        the specified teleport statistic.  Additionally, cost will be checked against the max value\n                        of the statistic rather than the minimum value when determining if the cost is affordable\n                        ',
                        tunable_type=bool,
                        default=False)),
                disabled_name='no_teleport_cost',
                enabled_name='specify_cost'),
            fade_duration=TunableSimMinute(
                description=
                '\n                Default fade time (in sim minutes) for the fading of the Sim\n                to happen.',
                default=0.5)))

    @classmethod
    def get_teleport_data(cls, teleport_type):
        return cls.TELEPORT_DATA_MAPPING.get(teleport_type)
Exemple #21
0
class SocialJigAnimation(AutoFactoryInit, HasTunableSingletonFactory):
    @staticmethod
    def on_tunable_loaded_callback(instance_class, tunable_name, source,
                                   value):
        animation_element = value.canonical_animation
        asm_key = animation_element.asm_key
        actor_name = animation_element.actor_name
        target_name = animation_element.target_name
        state_name = animation_element.begin_states[0]
        actor = StubActor(1)
        target = StubActor(2)
        animation_context = get_throwaway_animation_context()
        asm = create_asm(asm_key, context=animation_context)
        asm.set_actor(actor_name, actor)
        asm.set_actor(target_name, target)
        for posture_manifest_entry in asm.get_supported_postures_for_actor(
                actor_name).get_constraint_version():
            for posture_type in posture_manifest_entry.posture_types:
                if posture_type.mobile:
                    break
            break
        else:
            posture_type = None
        available_transforms = []
        if posture_type is not None:
            posture = posture_type(actor,
                                   None,
                                   PostureTrack.BODY,
                                   animation_context=animation_context)
            boundary_conditions = asm.get_boundary_conditions_list(
                actor, state_name, posture=posture, target=target)
            for (_, slots_to_params_entry) in boundary_conditions:
                if not slots_to_params_entry:
                    continue
                for (boundary_condition_entry,
                     param_sequences_entry) in slots_to_params_entry:
                    (relative_transform, _, _,
                     _) = boundary_condition_entry.get_transforms(asm, target)
                    available_transforms.append(
                        (param_sequences_entry, relative_transform))
        setattr(value, 'available_transforms', available_transforms)

    FACTORY_TUNABLES = {
        'canonical_animation':
        TunableAnimationReference(
            description=
            "\n            The canonical animation element used to generate social positioning\n            for this group.\n            \n            The animation must include a target actor (such as 'y') whose\n            relative positioning from an actor, such as 'x' defines the\n            positioning.\n            ",
            callback=None),
        'reverse_actor_sim_orientation':
        Tunable(
            description=
            '\n            If checked then we will reverse the orientation of the actor Sim\n            when generating this jig. \n            ',
            tunable_type=bool,
            default=False),
        'callback':
        on_tunable_loaded_callback
    }

    def _get_available_transforms_gen(self, actor, target):
        animation_element = self.canonical_animation
        actor_name = animation_element.actor_name
        target_name = animation_element.target_name
        actor_age = actor.age.age_for_animation_cache
        target_age = target.age.age_for_animation_cache
        locked_params = {
            ('species', actor_name):
            SpeciesExtended.get_animation_species_param(
                actor.extended_species),
            ('age', actor_name):
            actor_age.animation_age_param,
            ('species', target_name):
            SpeciesExtended.get_animation_species_param(
                target.extended_species),
            ('age', target_name):
            target_age.animation_age_param
        }
        for (param_sequences, transform) in self.available_transforms:
            for param_sequence in param_sequences:
                if not do_params_match(param_sequence, locked_params):
                    continue
                jig_params = frozendict({
                    param: value
                    for (param, value) in param_sequence.items()
                    if param not in locked_params
                })
                yield (transform, jig_params)

    def get_transforms_gen(self,
                           actor,
                           target,
                           fallback_routing_surface=None,
                           fgl_kwargs=None,
                           **kwargs):
        reserved_space_a = get_default_reserve_space(actor.species, actor.age)
        reserved_space_b = get_default_reserve_space(target.species,
                                                     target.age)
        fgl_kwargs = fgl_kwargs if fgl_kwargs is not None else {}
        ignored_objects = {actor.id, target.id}
        ignored_ids = fgl_kwargs.get('ignored_object_ids')
        if ignored_ids is not None:
            ignored_objects.update(ignored_ids)
        fgl_kwargs['ignored_object_ids'] = ignored_objects
        for (transform,
             jig_params) in self._get_available_transforms_gen(actor, target):
            actor_angle = yaw_quaternion_to_angle(transform.orientation)
            (translation_a, orientation_a, translation_b, orientation_b,
             routing_surface) = generate_jig_polygon(
                 actor.location,
                 transform.translation,
                 actor_angle,
                 target.location,
                 Vector2.ZERO(),
                 0,
                 reserved_space_a.left,
                 reserved_space_a.right,
                 reserved_space_a.front,
                 reserved_space_a.back,
                 reserved_space_b.left,
                 reserved_space_b.right,
                 reserved_space_b.front,
                 reserved_space_b.back,
                 fallback_routing_surface=fallback_routing_surface,
                 reverse_nonreletive_sim_orientation=self.
                 reverse_actor_sim_orientation,
                 **fgl_kwargs)
            if translation_a is None:
                continue
            yield (Transform(translation_a, orientation_a),
                   Transform(translation_b,
                             orientation_b), routing_surface, jig_params)

    def get_footprint_polygon(self, sim_a, sim_b, sim_a_transform,
                              sim_b_transform, routing_surface):
        reserved_space_a = get_default_reserve_space(sim_a.species, sim_a.age)
        reserved_space_b = get_default_reserve_space(sim_b.species, sim_b.age)
        polygon = _generate_poly_points(
            sim_a_transform.translation,
            sim_a_transform.orientation.transform_vector(Vector3.Z_AXIS()),
            sim_b_transform.translation,
            sim_b_transform.orientation.transform_vector(Vector3.Z_AXIS()),
            reserved_space_a.left, reserved_space_a.right,
            reserved_space_a.front, reserved_space_a.back,
            reserved_space_b.left, reserved_space_b.right,
            reserved_space_b.front, reserved_space_b.back)
        return PolygonFootprint(
            polygon,
            routing_surface=sim_a.routing_surface,
            cost=25,
            footprint_type=FootprintType.FOOTPRINT_TYPE_OBJECT,
            enabled=True)
Exemple #22
0
class RouteEventTypeAnimation(RouteEventDataBase, HasTunableFactory,
                              AutoFactoryInit):
    FACTORY_TUNABLES = {
        'animation_element':
        TunableAnimationReference(
            description=
            '\n            The animation that Sims play during the Route Event.\n            ',
            callback=None,
            class_restrictions=()),
        '_duration_override':
        OptionalTunable(
            description=
            "\n            If enabled, we override the must run duration we expect this route\n            event to take. We do this for animations that will freeze the\n            locomotion so that we don't actually take time away from the rest of\n            the path where other route events could play.\n            ",
            tunable=TunableRange(
                description=
                '\n                The duration we want this route event to have. This modifies how\n                much of the route time this event will take up to play the\n                animation. For route events that freeze locomotion, you might\n                want to set this to a very low value. Bear in mind that high\n                values are less likely to be scheduled for shorter routes.\n                ',
                tunable_type=float,
                default=0.1,
                minimum=0.1)),
        'target_participant':
        OptionalTunable(
            description=
            '\n            The target of the animation based on the resolver of the actor\n            playing the route event.\n            ',
            tunable=TunableEnumEntry(
                description=
                '\n                The participant related to the actor that plays the route event.\n                ',
                tunable_type=ParticipantType,
                default=ParticipantType.ObjectChildren))
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.arb = None
        self._duration_total = MAX_INT32
        self._duration_must_run = MAX_INT32
        self._duration_repeat = MAX_INT32

    @classmethod
    def test(cls, actor, event_data_tuning, ignore_carry=False):
        if actor is None:
            return TestResult(False, 'Route Event Actor is None.')
        if actor.is_sim:
            postures = event_data_tuning.animation_element.get_supported_postures(
            )
            sim_posture_state = actor.posture_state
            provided_postures = sim_posture_state.body.get_provided_postures(
                surface_target=MATCH_NONE)
            supported_postures = provided_postures.intersection(postures)
            if not supported_postures:
                return TestResult(
                    False, 'Animation Route Event does not support {} for {}.',
                    actor.posture_state, actor)
            if not ignore_carry:
                carry_state = sim_posture_state.get_carry_state()
                if not any(
                        are_carry_compatible(entry, carry_state)
                        for entry in supported_postures):
                    return TestResult(
                        False,
                        'Animation Route Event does not support {} for {}.',
                        actor.posture_state, actor)
        return TestResult.TRUE

    @property
    def duration_override(self):
        if self._duration_override is not None:
            return self._duration_override
        return self._duration_must_run

    def get_target(self, actor):
        if self.target_participant is None:
            return
        else:
            if actor.is_sim:
                resolver = SingleSimResolver(actor.sim_info)
            else:
                resolver = SingleObjectResolver(actor)
            targets = resolver.get_participants(self.target_participant)
            if targets:
                return next(iter(targets))

    def prepare(self, actor, setup_asm_override=None):
        def restart_asm(asm):
            asm.set_current_state('entry')
            return True

        target = self.get_target(actor)
        routing_component = actor.routing_component
        if actor.is_sim:
            route_interaction = routing_component.route_interaction
            if route_interaction is None:
                logger.error('Route Interaction was None for {}', actor)
                return
            route_event_animation = self.animation_element(
                route_interaction,
                setup_asm_additional=restart_asm
                if setup_asm_override is None else setup_asm_override,
                enable_auto_exit=False)
            asm = route_event_animation.get_asm()
            if asm is not None and target is not None and not asm.set_actor(
                    route_event_animation.target_name, target):
                logger.error('Route Event {} Failed to setup target.', self)
                return
            if asm is None:
                logger.warn(
                    'Unable to get a valid Route Event ASM ({}) for {}.',
                    route_event_animation, actor)
                return
        else:
            route_event_animation = self.animation_element(
                actor,
                target=target,
                setup_asm_func=restart_asm
                if setup_asm_override is None else setup_asm_override)
            animation_context = routing_component.animation_context
            asm = route_event_animation.get_asm(
                animation_context=animation_context)
            if asm is None:
                logger.warn(
                    'Unable to get a valid Route Event ASM ({}) for {}.',
                    route_event_animation, actor)
                return
        self.arb = Arb()
        route_event_animation.append_to_arb(asm, self.arb)
        route_event_animation.append_exit_to_arb(asm, self.arb)
        if self.arb is None:
            logger.error('Unable to create arb for Route Event: {}', self)
            return
        (self._duration_total, self._duration_must_run,
         self._duration_repeat) = self.arb.get_timing()

    def is_valid_for_scheduling(self, actor, path):
        if self.arb is None or self.arb.empty:
            return False
        return True

    def execute(self, actor, **kwargs):
        if actor.primitives:
            for primitive in tuple(actor.primitives):
                if isinstance(primitive, FollowPath):
                    primitive.set_animation_sleep_end(self._duration_must_run)
                    return

    def process(self, actor):
        if self.arb is not None:
            distribute_arb_element(self.arb, master=actor, immediate=True)
Exemple #23
0
 def __init__(self, description='A response that plays on both Sims.', **kwargs):
     super().__init__(animation=TunableAnimationReference(interaction_asm_type=InteractionAsmType.Response, participant_enum_override=(ParticipantTypeResponsePaired, ParticipantTypeResponsePaired.TargetSim), description='The response animation to play.'), description=description, **kwargs)