def __init__(self, *args, initiated=True, social_group=None, picked_object=None, source_social_si=None, set_work_timestamp=False, **kwargs):
     SuperInteraction.__init__(self, initiated=initiated, set_work_timestamp=set_work_timestamp, *args, **kwargs)
     SocialInteractionMixin.__init__(self, initiated=initiated, *args, **kwargs)
     self._initiated = initiated
     self._target_si = None
     self._target_si_test_result = True
     self._picked_object_ref = picked_object.ref() if picked_object is not None else None
     self._social_group = social_group
     self._source_social_si = source_social_si
     self._waiting_start_time = None
     self._waiting_alarm = None
     self._go_nearby_interaction = None
     self._last_go_nearby_time = None
     self._trying_to_go_nearby_target = False
     self._target_was_going_far_away = False
     self._waved_hi = False
class NewObjectTuning:
    NEW_OBJECT_COMMODITY = Commodity.TunableReference()
    NEW_OBJECT_AFFORDANCES = TunableSet(
        description=
        '\n        Affordances available on an object as long as its considered as new.\n        ',
        tunable=SuperInteraction.TunableReference(
            description=
            '\n            Affordance reference to add to new objects.\n            ',
            pack_safe=True))
class _PutDownBehaviorRunInteraction(HasTunableSingletonFactory,
                                     AutoFactoryInit):
    FACTORY_TUNABLES = {
        'affordance':
        OptionalTunable(
            description=
            '\n            The interaction to run once the Sim is put down.\n            ',
            tunable=SuperInteraction.TunableReference(
                description=
                '\n                The interaction to run once the Sim is put down.\n                '
            ),
            disabled_name='Use_Default_Affordance',
            enabled_name='Use_Specific_Affordance')
    }

    def get_provided_posture(self):
        if self.affordance is None:
            return posture_graph.SIM_DEFAULT_POSTURE_TYPE
        return self.affordance.provided_posture_type

    def get_target_si(self, interaction):
        sim = interaction.carry_target or interaction.target
        if self.affordance is None:
            interaction = sim.create_default_si()
        else:
            for running_interaction in sim.get_all_running_and_queued_interactions(
            ):
                if not running_interaction.transition is None:
                    if running_interaction.is_finishing:
                        continue
                    if running_interaction.get_interaction_type(
                    ) is not self.affordance:
                        continue
                    if running_interaction.target is not interaction.target:
                        continue
                    return (running_interaction, TestResult.TRUE)
            context = interaction.context.clone_for_sim(sim,
                                                        carry_target=None,
                                                        continuation_id=None)
            aop = AffordanceObjectPair(self.affordance, interaction.target,
                                       self.affordance, None)
            interaction = aop.interaction_factory(context).interaction
        return (interaction, TestResult.TRUE)
class AttractorPointInteraction(SuperInteraction):
    INSTANCE_TUNABLES = {
        '_attractor_point_identifier':
        TunableEnumWithFilter(
            description=
            '\n            The identifier that will be used to select which attractor points\n            we will use.\n            ',
            tunable_type=Tag,
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            filter_prefixes=('AtPo', ),
            tuning_group=GroupNames.PICKERTUNING),
        '_attractor_point_interaction':
        SuperInteraction.TunableReference(
            description=
            '\n            The affordance that we will push on the Sim once the attractor\n            point selection has been made.\n            ',
            tuning_group=GroupNames.PICKERTUNING)
    }

    @classmethod
    def _test(cls, target, context, **interaction_parameters):
        attractor_points = list(
            services.object_manager().get_objects_with_tag_gen(
                cls._attractor_point_identifier))
        if not attractor_points:
            return results.TestResult(
                False, 'No attractor points with tag {} found.',
                cls._attractor_point_identifier)
        return super()._test(target, context, **interaction_parameters)

    def _run_interaction_gen(self, timeline):
        attractor_points = list(
            services.object_manager().get_objects_with_tag_gen(
                self._attractor_point_identifier))
        chosen_point = random.choice(attractor_points)
        context = self.context.clone_for_continuation(self)
        self.sim.push_super_affordance(self._attractor_point_interaction,
                                       chosen_point, context)
        return True
        yield
class GroupInteractionSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'pre_situation_state':
        TunableTuple(
            description=
            '\n            Information related to the pre interaction situation state.\n            ',
            situation_state=_PreSituationState.TunableFactory(
                description=
                '\n                The pre-interaction situation state. Get everyone to their positions\n                and idle.\n                '
            ),
            time_out=TunableSimMinute(
                description=
                '\n                How long this will last.\n                ',
                default=15,
                minimum=1),
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'constraint_affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The interaction that puts the followers into the constraint.\n            '
        ),
        'constraint_leader_affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The interaction that puts the leader into the constraint.\n            '
        ),
        'leader_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for leader.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'member_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for member.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'interaction_state':
        _InteractionState.TunableFactory(
            description=
            '\n            The state that sim is doing the interaction.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The affordance for leader sim to run when all sims have gathered.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP)
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._target_object = None
        self._routing_sims = []

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.DONT

    def start_situation(self):
        super().start_situation()
        self._create_situation_geometry()
        self._change_state(self.pre_situation_state.situation_state())

    @classmethod
    def _states(cls):
        return (SituationStateData(
            1,
            _PreSituationState,
            factory=cls.pre_situation_state.situation_state),
                SituationStateData(2,
                                   _InteractionState,
                                   factory=cls.interaction_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.pre_situation_state.situation_state._tuned_values.
                    job_and_role_changes.items())

    @property
    def should_route_sims_on_add(self):
        return False

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._route_sim(sim)

    def _get_ignored_object_ids(self):
        ignored_sim_ids = [sim.id for sim in self.all_sims_in_situation_gen()]
        return ignored_sim_ids

    def get_next_interaction_state(self):
        return self.interaction_state

    def _create_situation_geometry(self):
        seed = self._seed
        default_target_id = seed.extra_kwargs.get('default_target_id', None)
        if default_target_id is not None:
            self._target_object = services.object_manager().get(
                default_target_id)
        if self._target_object is None:
            default_location = seed.extra_kwargs.get('default_location', None)
            if default_location is not None:
                self._target_object = TerrainPoint(default_location)
            else:
                logger.error('Failed to determine target for {}', self)
                self._self_destruct()
                return
        else:
            leader_sim = self.initiating_sim_info.get_sim_instance()
            if leader_sim is None:
                logger.error('No leader sim for {}', self)
                self._self_destruct()
                return

    def _route_sim(self, sim):
        interaction_context = InteractionContext(
            sim, InteractionSource.SCRIPT_WITH_USER_INTENT, Priority.High)
        leader_sim = self.initiating_sim_info.get_sim_instance()
        if leader_sim is sim:
            affordance = self.constraint_leader_affordance
        else:
            affordance = self.constraint_affordance
        aop = AffordanceObjectPair(affordance, self._target_object, affordance,
                                   None)
        aop.test_and_execute(interaction_context)
        self._routing_sims.append(sim.id)

    def on_sim_finish_routing(self, sim_info):
        if sim_info.id in self._routing_sims:
            self._routing_sims.remove(sim_info.id)
            if not self._routing_sims:
                next_state = self.get_next_interaction_state()
                self._change_state(next_state())
        else:
            logger.error(
                'Sim {} finishes routing but not in routing sim list of situation {}',
                sim_info, self)

    def _cancel_constraint_affordance_for_sim(self, sim):
        for si in sim.get_all_running_and_queued_interactions():
            if si.affordance is self.constraint_affordance:
                si.cancel(
                    FinishingType.SITUATIONS,
                    cancel_reason_msg='Group Interaction Situation done.')
            if si.affordance is self.constraint_leader_affordance:
                si.cancel(
                    FinishingType.SITUATIONS,
                    cancel_reason_msg='Group Interaction Situation done.')

    def _on_remove_sim_from_situation(self, sim):
        self._cancel_constraint_affordance_for_sim(sim)
        super()._on_remove_sim_from_situation(sim)

    def _destroy(self):
        for sim in self.all_sims_in_situation_gen():
            self._cancel_constraint_affordance_for_sim(sim)
        self._target_object = None
        super()._destroy()
class CreateCarriedObjectSuperInteraction(SuperInteraction):
    __qualname__ = 'CreateCarriedObjectSuperInteraction'
    INSTANCE_TUNABLES = {
        'definition':
        TunableReference(
            description='\n            The object to create.\n            ',
            tuning_group=GroupNames.CREATE_CARRYABLE,
            manager=services.definition_manager()),
        'carry_track_override':
        OptionalTunable(
            description=
            '\n            If enabled, specify which carry track the Sim must use to carry the\n            created object.\n            ',
            tuning_group=GroupNames.CREATE_CARRYABLE,
            tunable=TunableEnumEntry(
                description=
                '\n                Which hand to carry the object in.\n                ',
                tunable_type=PostureTrack,
                default=PostureTrack.RIGHT)),
        'initial_states':
        TunableList(
            description=
            '\n            A list of states to apply to the finished object as soon as it is\n            created.\n            ',
            tuning_group=GroupNames.CREATE_CARRYABLE,
            tunable=TunableStateValueReference()),
        'continuation':
        SuperInteraction.TunableReference(
            description=
            '\n            An interaction to push as a continuation to the carry.\n            '
        ),
        'continuation_with_affordance_overrides':
        OptionalTunable(
            description=
            "\n            If enabled, allows you to specify a continuation to the\n            carry based on a participant's object definition.\n            This continuation will be pushed in addition to the tunable continuation,\n            although you will rarely need to tune both at the same time.\n            ",
            tunable=TunableTuple(
                continuation=TunableContinuation(
                    description=
                    '\n                    A tunable continuation to push based on the parameters provided.\n                    '
                ),
                participant=TunableEnumEntry(
                    description=
                    '\n                    When using the affordance_override mapping, this\n                    is the participant we will use to get the definition.\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.PickedObject),
                affordance_override=TunableMapping(
                    description=
                    "\n                    Based on the participants's object definition, you can override\n                    the affordance on the tunable continuation.\n                    ",
                    key_type=TunableReference(
                        description=
                        '\n                        The object definition to look for.\n                        ',
                        manager=services.definition_manager()),
                    value_type=SuperInteraction.TunableReference())),
            tuning_group=GroupNames.CREATE_CARRYABLE)
    }

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

    @property
    def create_target(self):
        return self.definition

    @property
    def created_target(self):
        if self._object_create_helper is not None:
            return self._object_create_helper.object

    def _get_create_continuation_affordance(self):
        def create_continuation_affordance():
            context = self.context.clone_for_continuation(self)
            aop = AffordanceObjectPair(self.continuation, self.created_target,
                                       self.continuation, None)
            return (aop, context)

        if self.continuation is not None:
            return create_continuation_affordance

    def build_basic_content(self, sequence, **kwargs):
        super_build_basic_content = super().build_basic_content

        def setup_object(obj):
            for initial_state in reversed(self.initial_states):
                obj.set_state(initial_state.state, initial_state)
            obj.set_household_owner_id(self.sim.household.id)

        self._object_create_helper = CreateObjectHelper(
            self.sim,
            self.definition,
            self,
            init=setup_object,
            tag='CreateCarriedObjectSuperInteraction')

        def claim_object(*_, **__):
            self._object_create_helper.claim()

        def set_carry_target(_):
            if self.carry_track_override:
                self.track = self.carry_track_override
            else:
                self.track = DEFAULT
            if self.track is None:
                return False
            self.context.carry_target = self.created_target

        def push_tunable_continuation_with_affordance_overrides(_):
            if self.continuation_with_affordance_overrides is None:
                return
            obj = self.get_participant(
                self.continuation_with_affordance_overrides.participant)
            if obj is not None:
                affordance_override = self.continuation_with_affordance_overrides.affordance_override.get(
                    obj.definition)
            else:
                affordance_override = None
            interaction_parameters = {}
            if 'picked_item_ids' in self.interaction_parameters:
                interaction_parameters[
                    'picked_item_ids'] = self.interaction_parameters[
                        'picked_item_ids']
            self.push_tunable_continuation(
                self.continuation_with_affordance_overrides.continuation,
                affordance_override=affordance_override,
                **interaction_parameters)

        def enter_carry(timeline):
            result = yield element_utils.run_child(
                timeline,
                enter_carry_while_holding(
                    self,
                    self.created_target,
                    callback=claim_object,
                    create_si_fn=self._get_create_continuation_affordance(),
                    track=self.track,
                    sequence=build_critical_section(
                        super_build_basic_content(sequence, **kwargs),
                        flush_all_animations)))
            return result

        return (self._object_create_helper.create(
            set_carry_target, enter_carry,
            push_tunable_continuation_with_affordance_overrides),
                lambda _: self._object_create_helper.claimed)
class CreateCarriedObjectMixin:
    INTERACTION_PARAM_KEY = 'CreateCarriedObjectRuntimeObjectDefinition'
    INSTANCE_TUNABLES = {
        'definition':
        OptionalTunable(
            description=
            "\n            The object to create; this can be set at runtime.\n            \n            If 'runtime parameter' is chosen, it will look at the parameter \n            passed in at runtime to determine which object to create.\n            The primary use of the 'runtime parameter' option is if\n            the interaction is pushed from code so consult a GPE before using it.\n            ",
            tunable=TunableTestedVariant(
                description=
                '\n                The object to create.\n                ',
                tunable_type=ObjectDefinition(pack_safe=True)),
            tuning_group=GroupNames.CREATE_CARRYABLE,
            disabled_name='runtime_parameter',
            enabled_name='tuned_definition',
            enabled_by_default=True),
        'carry_track_override':
        OptionalTunable(
            description=
            '\n            If enabled, specify which carry track the Sim must use to carry the\n            created object.\n            ',
            tuning_group=GroupNames.CREATE_CARRYABLE,
            tunable=TunableEnumEntry(
                description=
                '\n                Which hand to carry the object in.\n                ',
                tunable_type=PostureTrack,
                default=PostureTrack.RIGHT)),
        'initial_states':
        TunableList(
            description=
            '\n            A list of states to apply to the finished object as soon as it is\n            created.\n            ',
            tuning_group=GroupNames.CREATE_CARRYABLE,
            tunable=TunableStateValueReference()),
        'continuation':
        SuperInteraction.TunableReference(
            description=
            '\n            An interaction to push as a continuation to the carry.\n            ',
            allow_none=True),
        'continuation_with_affordance_overrides':
        OptionalTunable(
            description=
            "\n            If enabled, allows you to specify a continuation to the\n            carry based on a participant's object definition.\n            This continuation will be pushed in addition to the tunable continuation,\n            although you will rarely need to tune both at the same time.\n            ",
            tunable=TunableTuple(
                continuation=TunableContinuation(
                    description=
                    '\n                    A tunable continuation to push based on the parameters provided.\n                    '
                ),
                participant=TunableEnumEntry(
                    description=
                    '\n                    When using the affordance_override mapping, this\n                    is the participant we will use to get the definition.\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.PickedObject),
                affordance_override=TunableMapping(
                    description=
                    "\n                    Based on the participants's object definition, you can override\n                    the affordance on the tunable continuation.\n                    ",
                    key_type=TunableReference(
                        description=
                        '\n                        The object definition to look for.\n                        ',
                        manager=services.definition_manager()),
                    value_type=SuperInteraction.TunableReference())),
            tuning_group=GroupNames.CREATE_CARRYABLE)
    }

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

    @property
    def create_target(self):
        return self._get_chosen_definition()

    def _get_chosen_definition(self):
        if self._chosen_definition is None:
            if self.definition is None:
                self._chosen_definition = self.interaction_parameters.get(
                    self.INTERACTION_PARAM_KEY, None)
            else:
                self._chosen_definition = self.definition(
                    resolver=self.get_resolver())
        return self._chosen_definition

    def _get_create_continuation_affordance(self):
        def create_continuation_affordance():
            context = self.context.clone_for_continuation(self)
            aop = AffordanceObjectPair(self.continuation, self.created_target,
                                       self.continuation, None)
            return (aop, context)

        def create_continuation_affordance_with_overrides():
            obj = self.get_participant(
                self.continuation_with_affordance_overrides.participant)
            if obj is not None:
                affordance_override = self.continuation_with_affordance_overrides.affordance_override.get(
                    obj.definition)
            else:
                affordance_override = None
            interaction_parameters = {}
            if 'picked_item_ids' in self.interaction_parameters:
                interaction_parameters[
                    'picked_item_ids'] = self.interaction_parameters[
                        'picked_item_ids']
            for continuation in reversed(
                    self.continuation_with_affordance_overrides.continuation):
                local_actors = self.get_participants(continuation.actor)
                if self.sim in local_actors:
                    aops_and_context = self.get_continuation_aop_and_context(
                        continuation,
                        self.sim,
                        affordance_override=affordance_override,
                        **interaction_parameters)
                    if aops_and_context:
                        return aops_and_context
            return (None, None)

        if self.continuation_with_affordance_overrides is not None:
            return create_continuation_affordance_with_overrides
        elif self.continuation is not None:
            return create_continuation_affordance

    def build_basic_content(self, sequence, **kwargs):
        super_build_basic_content = super().build_basic_content

        def setup_object(obj):
            for initial_state in reversed(self.initial_states):
                obj.set_state(initial_state.state, initial_state)
            obj.set_household_owner_id(self.sim.household.id)

        self._object_create_helper = CreateObjectHelper(
            self.sim,
            self._get_chosen_definition(),
            self,
            init=setup_object,
            tag='CreateCarriedObjectMixin')

        def claim_object(*_, **__):
            self._object_create_helper.claim()

        def set_carry_target(_):
            if self.carry_track_override:
                self.track = self.carry_track_override
            else:
                self.track = DEFAULT
            if self.track is None:
                return False
            self.map_create_target(self.created_target)

        def enter_carry(timeline):
            result = yield from element_utils.run_child(
                timeline,
                enter_carry_while_holding(
                    self,
                    self.created_target,
                    callback=claim_object,
                    create_si_fn=self._get_create_continuation_affordance(),
                    track=self.track,
                    sequence=build_critical_section(
                        super_build_basic_content(sequence, **kwargs),
                        flush_all_animations)))
            return result
            yield

        return (self._object_create_helper.create(set_carry_target,
                                                  enter_carry),
                lambda _: self._object_create_helper.claimed)
class GenericChooseBetweenTwoAffordancesSuperInteraction(ImmediateSuperInteraction):
    INSTANCE_TUNABLES = {'choice_dialog': UiDialogOkCancel.TunableFactory(description='\n            A Dialog that prompts the user with a two button dialog. The\n            chosen button will result in one of two affordances being chosen.\n            '), 'accept_affordance': SuperInteraction.TunablePackSafeReference(description='\n            The affordance to push on the sim if the user clicks on the \n            accept/ok button.\n            '), 'reject_affordance': SuperInteraction.TunablePackSafeReference(description='\n            The affordance to push on the Sim if the user chooses to click\n            on the reject/cancel button.\n            ')}

    @classmethod
    def _test(cls, target, context, **interaction_parameters):
        if cls.accept_affordance is None and cls.reject_affordance is None:
            return TestResult(False, 'The accept and reject affordances are unavailable with the currently installed packs.')
        return super()._test(target, context, **interaction_parameters)

    def _run_interaction_gen(self, timeline):
        context = self.context.clone_for_sim(self.sim, insert_strategy=interactions.context.QueueInsertStrategy.LAST)
        if self.accept_affordance is None or self.reject_affordance is None:
            affordance = self.accept_affordance or self.reject_affordance
            self.sim.push_super_affordance(affordance, target=self.target, context=context)
            yield

        def _on_response(dialog):
            affordance = self.accept_affordance if dialog.accepted else self.reject_affordance
            self.sim.push_super_affordance(affordance, target=self.target, context=context)

        dialog = self.choice_dialog(self.sim, resolver=SingleSimResolver(self.sim))
        dialog.show_dialog(on_response=_on_response)
Exemple #9
0
    class _ClubPickerActionChallenge(HasTunableSingletonFactory,
                                     AutoFactoryInit):
        FACTORY_TUNABLES = {
            'challenge_game':
            GameRules.TunableReference(
                description=
                '\n                The game that the club is being challenged at. This is used to\n                determine how many Sims are required, per team.\n                '
            ),
            'challenge_social_interaction':
            SocialSuperInteraction.TunableReference(
                description=
                '\n                Specify an interaction that the challenging Sim runs on a Sim in\n                the challenged club (usually the leader). Once this interaction\n                completes, the challenge executes.\n                '
            ),
            'challenge_interaction':
            SuperInteraction.TunableReference(
                description=
                '\n                The interaction to push on the Sims being challenged.\n                '
            )
        }

        def on_choice_selected(self, interaction, picked_items, **kwargs):
            club_service = services.get_club_service()
            if club_service is None:
                return
            actor_club_gathering = club_service.sims_to_gatherings_map.get(
                interaction.sim)
            if actor_club_gathering is None:
                return

            def _on_challenge_social_interaction_finished(
                    challenge_social_interaction):
                if not challenge_social_interaction.is_finishing_naturally:
                    return
                minimum_players_per_team = math.ceil(
                    self.challenge_game.players_per_game.lower_bound /
                    self.challenge_game.teams_per_game.lower_bound)
                maximum_players_per_team = math.floor(
                    self.challenge_game.players_per_game.upper_bound /
                    self.challenge_game.teams_per_game.upper_bound)
                teams = []
                for (_, club) in zip(
                        range(self.challenge_game.teams_per_game.upper_bound),
                        itertools.chain(
                            (actor_club_gathering.associated_club, ),
                            picked_items)):
                    club_gathering = club_service.clubs_to_gatherings_map.get(
                        club)
                    if club_gathering is None:
                        continue
                    club_team = set()
                    challenger_sims = (
                        interaction.sim,
                    ) if actor_club_gathering.associated_club is club else ()
                    for club_member in itertools.chain(
                            challenger_sims,
                            sorted(club_gathering.all_sims_in_situation_gen(),
                                   key=lambda sim: sim.sim_info is not club.
                                   leader)):
                        club_team.add(club_member)
                        if len(club_team) >= maximum_players_per_team:
                            break
                    if len(club_team) >= minimum_players_per_team:
                        teams.append(club_team)
                if len(teams) < self.challenge_game.teams_per_game.lower_bound:
                    return
                all_sims = tuple(itertools.chain.from_iterable(teams))
                game_transition_destination_node_validator = GameTransitionDestinationNodeValidator(
                    self.challenge_game, teams=teams)
                for sim in all_sims:
                    context = challenge_social_interaction.context.clone_for_sim(
                        sim,
                        group_id=challenge_social_interaction.group_id,
                        source_interaction_id=challenge_social_interaction.id,
                        source_interaction_sim_id=challenge_social_interaction.
                        sim.sim_id,
                        insert_strategy=QueueInsertStrategy.NEXT)
                    sim.push_super_affordance(
                        self.challenge_interaction,
                        interaction.target,
                        context,
                        game_transition_destination_node_validator=
                        game_transition_destination_node_validator)

            for club in picked_items:
                club_gathering = club_service.clubs_to_gatherings_map.get(club)
                if club_gathering is None:
                    continue
                for club_member in sorted(
                        club_gathering.all_sims_in_situation_gen(),
                        key=lambda sim: sim.sim_info is not club.leader):
                    context = interaction.context.clone_from_immediate_context(
                        interaction)
                    execute_result = interaction.sim.push_super_affordance(
                        self.challenge_social_interaction, club_member,
                        context)
                    if execute_result:
                        challenge_social_interaction = execute_result.interaction
                        challenge_social_interaction.register_on_finishing_callback(
                            _on_challenge_social_interaction_finished)
                        break
 def __init__(self,
              optional_create=True,
              description='An object definition and states to apply.',
              class_restrictions=(),
              **kwargs):
     definition = TunableReference(
         description='\n            An object to create.',
         manager=services.definition_manager(),
         class_restrictions=class_restrictions)
     if optional_create:
         definition = OptionalTunable(definition)
     super().__init__(
         definition=definition,
         carry_track=OptionalTunable(
             description=
             '\n                Which hand to carry the object in.',
             tunable=TunableEnumEntry(PostureTrack,
                                      default=PostureTrack.RIGHT)),
         initial_states=TunableList(
             description=
             '\n                A list of states to apply to the finished object as soon as it is created.\n                ',
             tunable=TunableStateValueReference()),
         apply_states=TunableList(
             description=
             '\n                A list of states to apply to the finished object.\n                ',
             tunable=TunableStateValueReference()),
         apply_tags=TunableSet(
             description=
             '\n                A list of category tags to apply to the finished product.\n                ',
             tunable=TunableEnumEntry(tag.Tag,
                                      tag.Tag.INVALID,
                                      description='What tag to test for')),
         conditional_apply_states=TunableList(
             description=
             '\n                A list of states to apply to the finished object based on if the associated test passes.\n                ',
             tunable=TunableTuple(
                 description=
                 '\n                    The test to pass for the given state to be applied.',
                 test=TunableTestVariant(),
                 state=TunableStateValueReference())),
         super_affordances=TunableList(
             description=
             '\n                Affordances available on the finished product.\n                ',
             tunable=SuperInteraction.TunableReference()),
         loot_list=TunableList(
             description=
             '\n                A list of pre-defined loot operations to apply after crafting object creation.\n                ',
             tunable=LootActions.TunableReference()),
         quality_adjustment=TunableQualityInfo(),
         simoleon_value_modifiers_map=TunableMapping(
             description=
             '\n                    The mapping of state values to Simolean value modifiers.\n                    The final value of a craftable is decided based on its\n                    retail value multiplied by the sum of all modifiers for\n                    states that apply to the final product. All modifiers are\n                    added together first, then the sum will be multiplied by\n                    the retail price.\n                    ',
             key_type=TunableStateValueReference(
                 description=
                 '\n                        The quality state values. If this item has this state,\n                        then a random modifier between min_value and max_value\n                        will be multiplied to the retail price.'
             ),
             value_type=TunableInterval(
                 description=
                 '\n                        The maximum modifier multiplied to the retail price based on the provided state value\n                        ',
                 tunable_type=float,
                 default_lower=1,
                 default_upper=1)),
         simoleon_value_skill_curve=TunableStatisticModifierCurve.
         TunableFactory(
             description=
             "\n                Allows you to adjust the final value of the object based on the Sim's\n                level of a given skill.\n                ",
             axis_name_overrides=('Skill Level', 'Simoleon Multiplier'),
             locked_args={'subject': ParticipantType.Actor}),
         masterworks=OptionalTunable(
             TunableTuple(
                 description=
                 '\n                    If the result of this recipe can be a masterwork (i.e. a\n                    masterpiece for paintings), this should be enabled.\n                    ',
                 base_test=event_testing.tests.TunableTestSet(
                     description=
                     "\n                        This is the initial gate for a recipe to result in a masterwork.\n                        If this test doesn't pass, we don't even attempt to be a masterwork.\n                        "
                 ),
                 base_chance=Tunable(
                     description=
                     '\n                        Once the Base Test passes, this will be the base chance\n                        that this recipe will result in a masterwork.\n                        0.0 is 0% chance\n                        1.0 is 100% chance\n                        ',
                     tunable_type=float,
                     default=0.25),
                 multiplier_tests=TunableList(
                     TunableTuple(
                         description=
                         '\n                        When deciding if this recipe will result in a masterwork, we run through each test in this list. IF the test passes, its multiplier will be applied to the Base Chance.\n                        ',
                         multiplier=Tunable(
                             description=
                             '\n                            This is the multiplier that will be applied to the Base Chance if these tests pass.\n                            ',
                             tunable_type=float,
                             default=1),
                         tests=event_testing.tests.TunableTestSet())),
                 skill_adjustment=Tunable(
                     description=
                     "\n                        The masterwork chance adjustment based on the effective skill\n                        level. For each level higher than the requires skill of the recipe,\n                        the masterwork chance will be increased by this.\n                        There is no penalty if, somehow, the required skill is greater\n                        than the effective skill of the Sim.\n                        Example:\n                        Assume you're level 10 writing skill and you're crafting a\n                        book that requires level 4 writing skill. Also assume skill_adjustment\n                        is tuned to 0.05 and the base_chance is set to 0.25.\n                        The differences in skill is 10 (your skill) - 4 (required skill) = 6 \n                        Then, take this difference, 6 * 0.05 (skill_adjustment) = 0.3\n                        The base chance, 0.25 + 0.3 = 0.55. 55% chance of this being a masterwork.\n                        ",
                     tunable_type=float,
                     default=0.0),
                 simoleon_value_multiplier=TunableInterval(
                     description=
                     '\n                        The amount by which the final simoleon value of the object will be multiplied if it is a masterwork.\n                        The multiplier is a random number between the lower and upper bounds, inclusively.\n                        ',
                     tunable_type=float,
                     default_lower=1,
                     default_upper=1))),
         thumbnail_geo_state=OptionalTunable(
             description=
             '\n                The geo state name override for recipe picker thumbnail generation.\n                ',
             disabled_name='UseCatalog',
             enabled_name='CustomValue',
             tunable=Tunable(description='Geo State name',
                             tunable_type=str,
                             default='')),
         thumbnail_material_state=OptionalTunable(
             description=
             '\n                The material state name override for recipe picker thumbnail generation.\n                ',
             disabled_name='UseCatalog',
             enabled_name='CustomValue',
             tunable=Tunable(description='Material State name',
                             tunable_type=str,
                             default='')),
         description=description,
         **kwargs)
class Phase(HasTunableReferenceFactory,
            HasTunableFactory,
            metaclass=TunedInstanceMetaclass,
            manager=services.get_instance_manager(sims4.resources.Types.RECIPE)
            ):
    __qualname__ = 'Phase'
    debug_name = 'uninitialized'
    TURN_BASED = 'turn_based'
    PROGRESS_BASED = 'progress_based'
    INSTANCE_SUBCLASSES_ONLY = True
    INSTANCE_TUNABLES = {
        'super_affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            Super-affordance that manages this phase of the recipe.\n            '
        ),
        'phase_interaction_name_override':
        OptionalTunable(
            description=
            "\n            The localized name that will display in the UI instead of the\n            recipe's Phase Interaction Name property.\n            ",
            tunable=sims4.localization.TunableLocalizedStringFactory()),
        'target_ico':
        Tunable(
            description=
            "\n            This phase targets the last object created by the recipe, so don't\n            run autonomy to find it.\n            ",
            tunable_type=bool,
            default=False),
        '_object_info':
        TunableVariant(
            description=
            '\n            If this phase creates an object, use this definition.\n            ',
            locked_args={
                'none': None,
                'use_final_product': DEFAULT
            },
            literal=TunableRecipeObjectInfo(
                description=
                '\n                If this phase creates an object, use this definition.\n                '
            ),
            default='none'),
        '_cancel_phase_name':
        TunableEnumEntry(
            description=
            '\n            The name of the phase to run if the crafting process is canceled\n            during this phase.  May be None.\n            ',
            tunable_type=PhaseName,
            default=None),
        'loop_by_orders':
        Tunable(
            description=
            '\n            Should loop in the phase if multiple orders?\n            ',
            tunable_type=bool,
            default=False),
        'point_of_no_return':
        Tunable(
            description=
            '\n            When crafting get to this phase, the final product will be created\n            no matter cancel SI or not\n            ',
            tunable_type=bool,
            default=False),
        'phase_display_name':
        TunableLocalizedString(
            description=
            '\n            Localized name of this phase for the crafting quality UI\n            '
        ),
        'is_visible':
        Tunable(
            description=
            '\n            If this phase will show on crafting quality UI\n            ',
            tunable_type=bool,
            default=False),
        'completion':
        TunableVariant(
            description=
            "\n            Controls how the phase completes, either when turns have elapsed or\n            based on the crafting progress statistic maxing out.  If the super\n            interaction for the phase isn't looping or staging, this value is\n            ignored.\n            ",
            locked_args={
                TURN_BASED: TURN_BASED,
                PROGRESS_BASED: PROGRESS_BASED
            },
            default=TURN_BASED)
    }
    FACTORY_TUNABLES = {
        'next_phases':
        TunableList(
            description=
            '\n            The names of the phases that can come next.  If empty, this will be the\n            final phase.\n            ',
            tunable=TunableEnumEntry(PhaseName, None))
    }
    FACTORY_TUNABLES.update(INSTANCE_TUNABLES)

    def __init__(self, *, recipe, phase_id, **kwargs):
        super().__init__(**kwargs)
        self.recipe = recipe
        self.id = phase_id

    def recipe_tuning_loaded(self):
        recipe = self.recipe
        next_phases = []
        for name in self.next_phases:
            if name in recipe.phases:
                next_phases.append(recipe.phases[name])
            else:
                logger.error(
                    "Unknown phase '{}' specified in next_phase_names for phase '{}' in '{}'.",
                    name, self.id, recipe)
        self.next_phases = next_phases
        if self._cancel_phase_name is None:
            self.cancel_phase = None
        elif self._cancel_phase_name in recipe.phases:
            self.cancel_phase = recipe.phases[self._cancel_phase_name]
        else:
            self.cancel_phase = None
            logger.error(
                "Unknown phase '{}' specified for cancel_phase_name for phase '{}' in '{}'.",
                self._cancel_phase_name, self.id, recipe)

    @property
    def interaction_name(self):
        if self.phase_interaction_name_override is not None:
            return self.phase_interaction_name_override
        return self.recipe.phase_interaction_name

    @property
    def object_info(self):
        object_info = self._object_info
        if object_info is None:
            return
        if object_info is DEFAULT:
            return self.recipe.final_product
        return object_info

    @property
    def object_info_is_final_product(self):
        return self._object_info is DEFAULT

    @property
    def num_turns(self):
        return self._num_turns

    @property
    def one_shot(self):
        return self.super_affordance.one_shot

    @property
    def turn_based(self):
        if self.one_shot:
            return False
        return self.completion == self.TURN_BASED

    @property
    def progress_based(self):
        if self.one_shot:
            return False
        return self.completion == self.PROGRESS_BASED

    def __repr__(self):
        return '<{} {}>'.format(type(self).__name__, self.id)

    @property
    def allows_multiple_orders(self):
        return False

    def accepting_more_orders(self, turns):
        return True

    @property
    def repeat_on_resume(self):
        from crafting.crafting_interactions import CraftingPhaseSuperInteractionMixin
        if issubclass(self.super_affordance, CraftingPhaseSuperInteractionMixin
                      ) and self.super_affordance.advance_phase_on_resume:
            return False
        return True
Exemple #12
0
class AggregateSuperInteraction(SuperInteraction):
    INSTANCE_TUNABLES = {
        'aggregated_affordances':
        TunableList(
            description=
            '\n                A list of affordances composing this aggregate.  Distance\n                estimation will be used to break ties if there are multiple\n                valid interactions at the same priority level.\n                ',
            tunable=TunableTuple(
                description=
                '\n                    An affordance and priority entry.\n                    ',
                priority=Tunable(
                    description=
                    '\n                        The relative priority of this affordance compared to\n                        other affordances in this aggregate.\n                        ',
                    tunable_type=int,
                    default=0),
                affordance=SuperInteraction.TunableReference(
                    description=
                    '\n                        The aggregated affordance.\n                        ',
                    pack_safe=True)),
            tuning_group=GroupNames.GENERAL),
        'sim_to_push_affordance_on':
        TunableEnumEntry(
            description=
            '\n                The Sim to push the affordance on.  If this is Actor, the\n                affordance will be pushed as a continuation of this.\n                ',
            tunable_type=ParticipantType,
            default=ParticipantType.Actor,
            tuning_group=GroupNames.TRIGGERS),
        'use_aggregated_affordance_constraints':
        Tunable(
            description=
            "\n            If enabled, this interaction will pull it's constraints from the\n            interaction constraints of the aggregated affordances. The benefit\n            is that we are compatible with interactions we intend to run, even\n            if they have constraints different from one another. This prevents\n            us from having to add a bunch of tests to those affordances and a\n            generic constraint here.\n            ",
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.CONSTRAINTS)
    }
    _allow_user_directed = True

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

    @classproperty
    def affordances(cls):
        return (a.affordance.get_interaction_type()
                for a in cls.aggregated_affordances)

    @classmethod
    def _aops_sorted_gen(cls, target, **interaction_parameters):
        affordances = []
        for aggregated_affordance in cls.aggregated_affordances:
            aop = AffordanceObjectPair(aggregated_affordance.affordance,
                                       target,
                                       aggregated_affordance.affordance, None,
                                       **interaction_parameters)
            affordances.append((aggregated_affordance.priority, aop))
        return sorted(affordances, key=operator.itemgetter(0), reverse=True)

    @flexmethod
    def _get_tested_aops(cls, inst, target, context, **interaction_parameters):
        inst_or_cls = inst if inst is not None else cls
        if inst is not None and inst._valid_aops is not None:
            return inst._valid_aops
        aops_valid = []
        cls._allow_user_directed = False
        for (priority,
             aop) in inst_or_cls._aops_sorted_gen(target,
                                                  **interaction_parameters):
            test_result = aop.test(context)
            if test_result:
                if aop.affordance.allow_user_directed:
                    cls._allow_user_directed = True
                aops_valid.append((aop, priority))
        if inst is not None:
            inst._valid_aops = aops_valid
        return aops_valid

    @flexmethod
    def test(cls,
             inst,
             target=DEFAULT,
             context=DEFAULT,
             super_interaction=None,
             skip_safe_tests=False,
             **interaction_parameters):
        inst_or_cls = inst if inst is not None else cls
        result = super(__class__,
                       inst_or_cls).test(target=target,
                                         context=context,
                                         super_interaction=super_interaction,
                                         skip_safe_tests=skip_safe_tests,
                                         **interaction_parameters)
        if result:
            target = target if target is not DEFAULT else inst.target
            context = context if context is not DEFAULT else inst.context
            context = context.clone_for_sim(
                cls.get_participant(
                    participant_type=cls.sim_to_push_affordance_on,
                    sim=context.sim,
                    target=target))
            valid_aops = inst_or_cls._get_tested_aops(target, context,
                                                      **interaction_parameters)
            result = TestResult.TRUE if valid_aops else TestResult(
                False, 'No sub-affordances passed their tests.')
        return result

    @classmethod
    def consumes_object(cls):
        for affordance_tuple in cls.aggregated_affordances:
            if affordance_tuple.affordance.consumes_object():
                return True
        return False

    @classproperty
    def allow_user_directed(cls):
        return cls._allow_user_directed

    @flexmethod
    def _constraint_gen(cls,
                        inst,
                        sim,
                        target,
                        participant_type=ParticipantType.Actor,
                        **kwargs):
        inst_or_cls = cls if inst is None else inst
        yield from super(SuperInteraction, inst_or_cls)._constraint_gen(
            sim, target, participant_type=participant_type, **kwargs)
        if inst_or_cls.use_aggregated_affordance_constraints:
            aggregated_constraints = []
            affordances = []
            affordances = [
                aop.super_affordance for (aop, _) in inst._valid_aops
            ]
            affordances = affordances if not inst is not None or not inst._valid_aops is not None or affordances else [
                affordance_tuple.affordance
                for affordance_tuple in inst_or_cls.aggregated_affordances
            ]
            if not affordances:
                yield Nowhere
            for aggregated_affordance in affordances:
                intersection = ANYWHERE
                constraint_gen = aggregated_affordance.constraint_gen
                constraint_gen = super(SuperInteraction,
                                       aggregated_affordance)._constraint_gen
                for constraint in constraint_gen(
                        sim,
                        inst_or_cls.get_constraint_target(target),
                        participant_type=participant_type,
                        **kwargs):
                    intersection = constraint.intersect(intersection)
                    if not intersection.valid:
                        continue
                aggregated_constraints.append(intersection)
            if aggregated_constraints:
                yield create_constraint_set(
                    aggregated_constraints,
                    debug_name='AggregatedConstraintSet')

    def _do_perform_gen(self, timeline):
        sim = self.get_participant(self.sim_to_push_affordance_on)
        if sim == self.context.sim:
            context = self.context.clone_for_continuation(self)
        else:
            context = context.clone_for_sim(sim)
        max_priority = None
        aops_valid = []
        self._valid_aops = None
        valid_aops = self._get_tested_aops(self.target, context,
                                           **self.interaction_parameters)
        for (aop, priority) in valid_aops:
            if max_priority is not None:
                if priority < max_priority:
                    break
            aops_valid.append(aop)
            max_priority = priority
        if not aops_valid:
            logger.warn(
                'Failed to find valid super affordance in AggregateSuperInteraction: {}, did we not run its test immediately before executing it?',
                self)
            return ExecuteResult.NONE
            yield
        compatible_interactions = []
        for aop in aops_valid:
            interaction_result = aop.interaction_factory(context)
            if not interaction_result:
                raise RuntimeError(
                    'Failed to generate interaction from aop {}. {} [rmccord]'.
                    format(aop, interaction_result))
            interaction = interaction_result.interaction
            if self.use_aggregated_affordance_constraints:
                if interactions.si_state.SIState.test_compatibility(
                        interaction, force_concrete=True):
                    compatible_interactions.append(interaction)
            compatible_interactions.append(interaction)
        if not compatible_interactions:
            return ExecuteResult.NONE
            yield
        interactions_by_distance = []
        for interaction in compatible_interactions:
            if len(compatible_interactions) == 1:
                distance = 0
            else:
                (distance, _, _) = interaction.estimate_distance()
            if distance is not None:
                interactions_by_distance.append((distance, interaction))
            else:
                interactions_by_distance.append(
                    (sims4.math.MAX_INT32, interaction))
        (_, interaction) = min(interactions_by_distance,
                               key=operator.itemgetter(0))
        return AffordanceObjectPair.execute_interaction(interaction)
        yield