Example #1
0
 def __init__(self, interaction, *args, sequence=(), **kwargs):
     super().__init__(interaction, sequence=sequence, *args, **kwargs)
     self._placement_failed = False
     if self.reserve_object is not None:
         reserved_sim = self.interaction.get_participant(
             self.reserve_object)
     else:
         reserved_sim = None
     self._object_helper = CreateObjectHelper(
         reserved_sim,
         self.definition,
         self,
         init=self._setup_created_object)
Example #2
0
 def __init__(self, interaction, *args, sequence=(), **kwargs):
     super().__init__(interaction, sequence=sequence, *args, **kwargs)
     self._placement_failed = False
     if self.reserve_object is not None:
         reserved_sim = self.interaction.get_participant(self.reserve_object)
     else:
         reserved_sim = None
     self._object_helper = CreateObjectHelper(reserved_sim, self.definition, self, init=self._setup_created_object)
    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)
Example #4
0
 def initialize_helper(self, resolver, post_add=None):
     self._assigned_ownership.clear()
     self.resolver = resolver
     reserved_sim = None
     if self.reserve_object is not None:
         reserved_sim_info = self.resolver.get_participant(
             self.reserve_object)
         reserved_sim = reserved_sim_info.get_sim_instance()
     interaction = None
     if isinstance(self.resolver, InteractionResolver):
         interaction = self.resolver.interaction
     (self._definition,
      self._setup_params) = self.creation_data.get_creation_params(resolver)
     self._object_helper = CreateObjectHelper(
         reserved_sim,
         self._definition,
         interaction,
         object_to_clone=self.creation_data.get_source_object(
             self.resolver),
         init=self._setup_created_object,
         post_add=post_add)
    def build_basic_content(self, sequence=(), **kwargs):
        self.animation_context.register_event_handler(
            self._xevt_callback, handler_id=SCRIPT_EVENT_ID_START_CARRY)
        if self._aggregate_object_definition is None or self.carry_target is not None and self._aggregate_object_definition is self.carry_target.definition:
            if self.context.carry_target is None:
                self._setup_collected_object(self.target)
            return super().build_basic_content(sequence, **kwargs)
        if self.carry_target is not None:
            swap_carry = True
            self._original_carry_target = self.carry_target
        else:
            swap_carry = False
        self._object_create_helper = CreateObjectHelper(
            self.sim,
            self._aggregate_object_definition.id,
            self,
            post_add=self._setup_collected_object,
            tag='Aggregate object created for a CollectManyInteraction.')
        super_build_basic_content = super().build_basic_content

        def grab_sequence(timeline):
            nonlocal sequence
            sequence = super_build_basic_content(sequence)
            if swap_carry:
                sequence = swap_carry_while_holding(
                    self,
                    self._original_carry_target,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    sequence=sequence)
            else:
                sequence = enter_carry_while_holding(
                    self,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    create_si_fn=None,
                    sequence=sequence)
            result = yield element_utils.run_child(timeline, sequence)

        return self._object_create_helper.create(grab_sequence)
    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)
    def build_basic_content(self, sequence=(), **kwargs):
        self.animation_context.register_event_handler(self._xevt_callback, handler_id=SCRIPT_EVENT_ID_START_CARRY)
        if self._aggregate_object_definition is None or self.carry_target is not None and self._aggregate_object_definition is self.carry_target.definition:
            if self.context.carry_target is None:
                self._setup_collected_object(self.target)
            return super().build_basic_content(sequence, **kwargs)
        if self.carry_target is not None:
            swap_carry = True
            self._original_carry_target = self.carry_target
        else:
            swap_carry = False
        self._object_create_helper = CreateObjectHelper(self.sim, self._aggregate_object_definition.id, self, post_add=self._setup_collected_object, tag='Aggregate object created for a CollectManyInteraction.')
        super_build_basic_content = super().build_basic_content

        def grab_sequence(timeline):
            nonlocal sequence
            sequence = super_build_basic_content(sequence)
            if swap_carry:
                sequence = swap_carry_while_holding(self, self._original_carry_target, self.created_target, callback=self._object_create_helper.claim, sequence=sequence)
            else:
                sequence = enter_carry_while_holding(self, self.created_target, callback=self._object_create_helper.claim, create_si_fn=None, sequence=sequence)
            result = yield element_utils.run_child(timeline, sequence)

        return self._object_create_helper.create(grab_sequence)
Example #8
0
class ObjectCreationMixin:
    INVENTORY = 'inventory'
    CARRY = 'carry'
    INSTANCE_TUNABLES = FACTORY_TUNABLES = {
        'creation_data':
        TunableObjectCreationDataVariant(
            description=
            '\n            Define the object to create.\n            '),
        'initial_states':
        TunableList(
            description=
            '\n            A list of states to apply to the object as soon as it is created.\n            ',
            tunable=TunableTuple(
                description=
                '\n                The state to apply and optional tests to decide if the state\n                should apply.\n                ',
                state=TunableStateValueReference(),
                tests=OptionalTunable(
                    description=
                    '\n                    If enabled, the state will only get set on the created\n                    object if the tests pass. Note: These tests can not be\n                    performed on the newly created object.\n                    ',
                    tunable=TunableTestSet()))),
        'destroy_on_placement_failure':
        Tunable(
            description=
            "\n            If checked, the created object will be destroyed on placement failure.\n            If unchecked, the created object will be placed into an appropriate\n            inventory on placement failure if possible.  If THAT fails, object\n            will be destroyed.\n            By default it goes into location target's inventory, you can use \n            fallback_location_target_override to make the created object go to\n            another participant's inventory.\n            ",
            tunable_type=bool,
            default=False),
        'owner_sim':
        TunableEnumEntry(
            description=
            '\n            The participant Sim whose household should own the object. Leave this\n            as Invalid to not assign ownership.\n            ',
            tunable_type=ParticipantTypeSingleSim,
            default=ParticipantType.Invalid),
        'location':
        TunableVariant(
            description=
            '\n            Where the object should be created.\n            ',
            default='position',
            position=_PlacementStrategyLocation.TunableFactory(),
            slot=_PlacementStrategySlot.TunableFactory(),
            inventory=TunableTuple(
                description=
                '\n                An inventory based off of the chosen Participant Type.\n                ',
                locked_args={'location': INVENTORY},
                location_target=TunableEnumEntry(
                    description=
                    '\n                    "The owner of the inventory the object will be created in."\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.Actor),
                mark_object_as_stolen_from_career=Tunable(
                    description=
                    '\n                    Marks the object as stolen from a career by the tuned location_target participant.\n                    This should only be checked if this basic extra is on a CareerSuperInteraction.\n                    ',
                    tunable_type=bool,
                    default=False),
                place_in_hidden_inventory=Tunable(
                    description=
                    '\n                    If True, the object is placed in the hidden inventory rather than the user-facing inventory.\n                    ',
                    tunable_type=bool,
                    default=False)),
            carry=TunableTuple(
                description=
                '\n                Carry the object. Note: This expects an animation in the\n                interaction to trigger the carry.\n                ',
                locked_args={'location': CARRY},
                carry_track_override=OptionalTunable(
                    description=
                    '\n                    If enabled, specify which carry track the Sim must use to carry the\n                    created object.\n                    ',
                    tunable=TunableEnumEntry(
                        description=
                        '\n                        Which hand to carry the object in.\n                        ',
                        tunable_type=PostureTrackGroup,
                        default=PostureTrack.RIGHT)))),
        'reserve_object':
        OptionalTunable(
            description=
            '\n            If this is enabled, the created object will be reserved for use by\n            the set Sim.\n            ',
            tunable=TunableEnumEntry(
                tunable_type=ParticipantTypeActorTargetSim,
                default=ParticipantTypeActorTargetSim.Actor)),
        'fallback_location_target_override':
        OptionalTunable(
            description=
            "\n            This will be ignored if destroy_on_placement_failure is checked. If this is enabled, we override fallback\n            location target.\n            Currently this is used when location target is different with the target whose inventory we want this\n            created object to go into. For example we want to create an object near another object but we want this\n            object to go to actor's inventory when placement fails.\n            ",
            tunable=TunableEnumEntry(tunable_type=ParticipantType,
                                     default=ParticipantType.Actor)),
        'notification_inventory':
        OptionalTunable(
            description=
            '\n            The notification to show when created object is placed in an inventory.\n            ',
            tunable=TunableTuple(
                participant_inventory=UiDialogNotification.TunableFactory(
                    description=
                    "\n                    The notification to show when created object is placed in a participant's (such as sim's) inventory.\n                    "
                ),
                household_inventory=UiDialogNotification.TunableFactory(
                    description=
                    '\n                    The notification to show when created object is placed in a household inventory.\n                    '
                ))),
        'temporary_tags':
        OptionalTunable(
            description=
            '\n            If enabled, these Tags are added to the created object and DO NOT\n            persist.\n            ',
            tunable=TunableSet(
                description=
                '\n                A set of temporary tags that are added to the created object.\n                These tags DO NOT persist.\n                ',
                tunable=TunableEnumEntry(
                    description=
                    '\n                    A tag that is added to the created object. This tag DOES\n                    NOT persist.\n                    ',
                    tunable_type=Tag,
                    default=Tag.INVALID),
                minlength=1)),
        'require_claim':
        Tunable(
            description=
            "\n            If checked, the created object will be claimed, and will need to\n            be reclaimed on load.  If it isn't reclaimed on load, the object\n            will be destroyed.\n            ",
            tunable_type=bool,
            default=False),
        'set_sim_as_owner':
        Tunable(
            description=
            '\n            If checked and owner_sim is set, the sim will also be set on the\n            object ownership component and not just the household.\n            ',
            tunable_type=bool,
            default=False),
        'set_value_to_crafted_tooltip':
        Tunable(
            description=
            '\n            If checked, the value will be set to the tooltip if this item has\n            a crafting component.\n            ',
            tunable_type=bool,
            default=True)
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.resolver = None
        self._object_helper = None
        self._assigned_ownership = set()
        self._definition = None
        self._setup_params = None

    def initialize_helper(self, resolver, post_add=None):
        self._assigned_ownership.clear()
        self.resolver = resolver
        reserved_sim = None
        if self.reserve_object is not None:
            reserved_sim_info = self.resolver.get_participant(
                self.reserve_object)
            reserved_sim = reserved_sim_info.get_sim_instance()
        interaction = None
        if isinstance(self.resolver, InteractionResolver):
            interaction = self.resolver.interaction
        (self._definition,
         self._setup_params) = self.creation_data.get_creation_params(resolver)
        self._object_helper = CreateObjectHelper(
            reserved_sim,
            self._definition,
            interaction,
            object_to_clone=self.creation_data.get_source_object(
                self.resolver),
            init=self._setup_created_object,
            post_add=post_add)

    @property
    def definition(self):
        return self.creation_data.get_definition(self.resolver)

    def create_object(self, resolver):
        self.initialize_helper(resolver, post_add=self._place_object)
        created_object = self._object_helper.create_object()
        self._object_helper = None
        return created_object

    def _setup_created_object(self, created_object):
        self.creation_data.setup_created_object(self.resolver, created_object,
                                                **self._setup_params)
        if self.owner_sim != ParticipantType.Invalid:
            owner_sim = self.resolver.get_participant(self.owner_sim)
            if owner_sim is not None and owner_sim.is_sim:
                created_object.update_ownership(
                    owner_sim, make_sim_owner=self.set_sim_as_owner)
                self._assigned_ownership.add(created_object.id)
        for initial_state in self.initial_states:
            if created_object.state_component is None:
                created_object.add_component(StateComponent(created_object))
            if not initial_state.tests is None:
                if initial_state.tests.run_tests(self.resolver):
                    if created_object.has_state(initial_state.state.state):
                        created_object.set_state(initial_state.state.state,
                                                 initial_state.state,
                                                 from_creation=True)
            if created_object.has_state(initial_state.state.state):
                created_object.set_state(initial_state.state.state,
                                         initial_state.state,
                                         from_creation=True)
        if self.temporary_tags is not None:
            created_object.append_tags(self.temporary_tags)
        if created_object.has_component(
                objects.components.types.CRAFTING_COMPONENT):
            created_object.crafting_component.update_simoleon_tooltip()
            created_object.crafting_component.update_quality_tooltip()
            if self.set_value_to_crafted_tooltip:
                created_object.update_tooltip_field(
                    TooltipFieldsComplete.simoleon_value,
                    created_object.current_value)
        created_object.update_object_tooltip()
        if self.require_claim:
            created_object.claim()

    def _get_ignored_object_ids(self):
        pass

    def _place_object_no_fallback(self, created_object):
        if hasattr(self.location, 'try_place_object'):
            ignored_object_ids = self._get_ignored_object_ids()
            return self.location.try_place_object(
                created_object,
                self.resolver,
                ignored_object_ids=ignored_object_ids)
        elif self.location.location == self.CARRY:
            return True
        return False

    def _get_fallback_location_target(self, created_object):
        if self.fallback_location_target_override is not None:
            target_override = self.resolver.get_participant(
                self.fallback_location_target_override)
            if target_override is not None:
                return target_override
            logger.error(
                'Fallback location target override for participant {} and created object {} is none.\n                                Invalid participant?',
                self.fallback_location_target_override, created_object)
        if hasattr(self.location, '_get_reference_objects_gen'):
            for obj in self.location._get_reference_objects_gen(
                    created_object, self.resolver):
                return obj
        return self.resolver.get_participant(self.location.location_target)

    def _place_object(self, created_object):
        self._setup_created_object(created_object)
        if self._place_object_no_fallback(created_object):
            return True
        if not self.destroy_on_placement_failure:
            participant = self._get_fallback_location_target(created_object)
            if participant.is_sim:
                if isinstance(participant, sims.sim_info.SimInfo):
                    participant = participant.get_sim_instance(
                        allow_hidden_flags=ALL_HIDDEN_REASONS)
            location_type = getattr(self.location, 'location', None)
            if location_type == self.INVENTORY and self.location.mark_object_as_stolen_from_career:
                interaction = self.resolver.interaction
                if interaction is None:
                    logger.error(
                        'Mark Object As Stolen From Career is checked on CreateObject loot {}. \n                                    This should only be check on basic extra in a CareerSuperInteraction.',
                        self)
                    return False
                career_uid = interaction.interaction_parameters.get(
                    'career_uid')
                if career_uid is not None:
                    career = interaction.sim.career_tracker.get_career_by_uid(
                        career_uid)
                    if career is not None:
                        name_data = career.get_career_location(
                        ).get_persistable_company_name_data()
                        text = None
                        guid = None
                        if isinstance(name_data, str):
                            text = name_data
                        else:
                            guid = name_data
                        MarkObjectAsStolen.mark_object_as_stolen(
                            created_object,
                            stolen_from_text=text,
                            stolen_from_career_guid=guid)
                else:
                    logger.error(
                        'Interaction {} is tuned with a CreateObject basic extra that has mark_object_as_stolen_from_career as True,\n                                    but is not a CareerSuperInteraction. This is not supported.',
                        interaction)
            if created_object.inventoryitem_component is not None:
                if created_object.id not in self._assigned_ownership:
                    if participant.is_sim:
                        participant_household_id = participant.household.id
                    else:
                        participant_household_id = participant.get_household_owner_id(
                        )
                    created_object.set_household_owner_id(
                        participant_household_id)
                    self._assigned_ownership.add(created_object.id)
                if participant.inventory_component.player_try_add_object(
                        created_object,
                        hidden=location_type == self.INVENTORY
                        and self.location.place_in_hidden_inventory):
                    if self.notification_inventory:
                        notification = self.notification_inventory.participant_inventory(
                            participant, self.resolver)
                        notification.show_dialog()
                    return True
            sim = self.resolver.get_participant(ParticipantType.Actor)
            if not (participant is not None and participant.inventory_component
                    is not None and sim is None or not sim.is_sim):
                owning_household = services.owning_household_of_active_lot()
                if owning_household is not None:
                    for sim_info in owning_household.sim_info_gen():
                        if sim_info.is_instanced():
                            sim = sim_info.get_sim_instance()
                            break
            if sim is not None:
                if not sim.is_npc:
                    try:
                        created_object.set_household_owner_id(sim.household.id)
                        if build_buy.move_object_to_household_inventory(
                                created_object):
                            if self.notification_inventory:
                                notification = self.notification_inventory.household_inventory(
                                    sim, self.resolver)
                                notification.show_dialog()
                            return True
                        logger.error(
                            'Creation: Failed to place object {} in household inventory.',
                            created_object,
                            owner='rmccord')
                    except KeyError:
                        pass
        return False
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)
    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 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)
Example #12
0
class CollectManyInteraction(SuperInteraction):
    INTERACTION_TARGET = 'interaction_target'
    INSTANCE_TUNABLES = {
        'aggregate_object':
        TunableVariant(
            description=
            '\n            The type of object to use as the aggregate object.  If a definition\n            is specified, the aggregate object will be created using that\n            definition.  If "interaction_target" is specified, the aggregate object\n            will be created using the definition of the interaction target.\n            ',
            definitions=TunableList(
                description=
                '\n                A list of object definitions. One of them will be chosen \n                randomly and created as part of this interaction to represent \n                the many collected objects the participant has picked up.\n                ',
                tunable=TunableReference(
                    manager=services.definition_manager()),
                unique_entries=True),
            locked_args={
                'interaction_target': INTERACTION_TARGET,
                'no_aggregate_object': None
            },
            default='no_aggregate_object'),
        'aggregate_object_owner':
        TunableEnumEntry(
            description=
            '\n            Specify the owner of the newly created aggregate object.\n            ',
            tunable_type=AggregateObjectOwnership,
            default=AggregateObjectOwnership.SAME_AS_TARGET),
        'destroy_original_object':
        Tunable(
            description=
            "\n            If checked, the original object (the target of this interaction),\n            will be destroyed and replaced with the specified aggregate object.\n            If unchecked, the aggregate object will be created in the Sim's\n            hand, but the original object will not be destroyed.\n            ",
            tunable_type=bool,
            default=True)
    }
    DIRTY_DISH_ACTOR_NAME = 'dirtydish'
    ITEMS_PARAM = 'items'
    _object_create_helper = None
    _collected_targets = WeakSet()

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

    @property
    def create_object_owner(self):
        return self.sim

    @property
    def _aggregate_object_definition(self):
        if self.aggregate_object is None:
            return
        if self.aggregate_object == self.INTERACTION_TARGET:
            return self.target.definition
        return random.choice(self.aggregate_object)

    @property
    def create_target(self):
        if self.context.carry_target is not None:
            return
        return self._aggregate_object_definition

    @property
    def created_target(self):
        return self.context.carry_target

    @classmethod
    def _test(cls, target, context, **interaction_parameters):
        if target is not None and target in cls._collected_targets:
            return TestResult(False, 'Target was already collected.')
        if cls.destroy_original_object and context.sim.posture_state.is_carrying(
                target):
            return TestResult(
                False, 'Target to destroy is being carried by this Sim.')
        return super()._test(target, context, **interaction_parameters)

    def setup_asm_default(self, asm, *args, **kwargs):
        result = super().setup_asm_default(asm, *args, **kwargs)
        if self.target is not None:
            surface_height = get_surface_height_parameter_for_object(
                self.target, sim=self.sim)
            asm.set_parameter('surfaceHeight', surface_height)
        if self._original_carry_target is not None:
            param_overrides = self._original_carry_target.get_param_overrides(
                self.DIRTY_DISH_ACTOR_NAME, only_for_keys=(self.ITEMS_PARAM, ))
            if param_overrides is not None:
                asm.update_locked_params(param_overrides)
        return result

    def build_basic_content(self, sequence=(), **kwargs):
        self.store_event_handler(self._xevt_callback,
                                 handler_id=SCRIPT_EVENT_ID_START_CARRY)
        if self._aggregate_object_definition is None or self.carry_target is not None and self._aggregate_object_definition is self.carry_target.definition:
            return super().build_basic_content(sequence, **kwargs)
        if self.carry_target is not None:
            swap_carry = True
            self._original_carry_target = self.carry_target
        else:
            swap_carry = False
        self._object_create_helper = CreateObjectHelper(
            self.sim,
            self._aggregate_object_definition.id,
            self,
            tag='Aggregate object created for a CollectManyInteraction.',
            init=self._setup_created_object)
        super_build_basic_content = super().build_basic_content

        def grab_sequence(timeline):
            nonlocal sequence
            sequence = super_build_basic_content(sequence)
            if swap_carry:
                sequence = swap_carry_while_holding(
                    self,
                    self._original_carry_target,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    sequence=sequence)
            else:
                sequence = enter_carry_while_holding(
                    self,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    create_si_fn=None,
                    sequence=sequence)
            _ = yield from element_utils.run_child(timeline, sequence)

        return self._object_create_helper.create(grab_sequence)

    def _setup_created_object(self, created_object):
        if self.aggregate_object_owner & AggregateObjectOwnership.SAME_AS_TARGET:
            if self.target is not None:
                created_object.set_household_owner_id(
                    self.target.get_household_owner_id())
        elif self.aggregate_object_owner & AggregateObjectOwnership.ACTIVE_HOUSEHOLD:
            active_household_id = services.active_household_id()
            if active_household_id is not None:
                created_object.set_household_owner_id(active_household_id)

    def _xevt_callback(self, *_, **__):
        if self.carry_target is not None:
            if self.target is not None:
                if self._object_create_helper is None:
                    for statistic in self.target.statistic_tracker:
                        self.carry_target.statistic_tracker.add_value(
                            statistic.stat_type, statistic.get_value())
                elif self._original_carry_target is not None:
                    for statistic in self._original_carry_target.statistic_tracker:
                        self.carry_target.statistic_tracker.add_value(
                            statistic.stat_type, statistic.get_value())
                elif self.aggregate_object is self.INTERACTION_TARGET:
                    self.carry_target.copy_state_values(self.target)
                else:
                    for statistic in self.target.statistic_tracker:
                        self.carry_target.statistic_tracker.set_value(
                            statistic.stat_type, statistic.get_value())
        if self.destroy_original_object and self.target is not None:
            self._collected_targets.add(self.target)
            self.target.transient = True
            self.target.remove_from_client()
        if self._original_carry_target is not None:
            self._collected_targets.add(self._original_carry_target)
            self._original_carry_target.transient = True
            self._original_carry_target.remove_from_client()

    @classproperty
    def requires_target_support(cls):
        return False
Example #13
0
class ObjectCreationElement(XevtTriggeredElement):
    __qualname__ = 'ObjectCreationElement'
    POSITION = 'position'
    INVENTORY = 'inventory'
    SLOT = 'slot'

    class ObjectDefinition(HasTunableSingletonFactory, AutoFactoryInit):
        __qualname__ = 'ObjectCreationElement.ObjectDefinition'
        FACTORY_TUNABLES = {
            'definition':
            TunableReference(
                description=
                '\n                The definition of the object that is created.\n                ',
                manager=services.definition_manager())
        }

        def get_definition(self):
            return self.definition

        def setup_created_object(self, interaction, created_object):
            pass

    class RecipeDefinition(HasTunableSingletonFactory, AutoFactoryInit):
        __qualname__ = 'ObjectCreationElement.RecipeDefinition'
        FACTORY_TUNABLES = {
            'recipe':
            TunableReference(
                description=
                '\n                The recipe to use to create the object.\n                ',
                manager=services.recipe_manager())
        }

        def get_definition(self):
            return self.recipe.final_product.definition

        def setup_created_object(self, interaction, created_object):
            from crafting.crafting_process import CraftingProcess
            crafting_process = CraftingProcess(crafter=interaction.sim,
                                               recipe=self.recipe)
            crafting_process.setup_crafted_object(created_object,
                                                  is_final_product=True)

    FACTORY_TUNABLES = {
        'description':
        '\n            Create an object as part of an interaction.\n            ',
        'creation_data':
        TunableVariant(
            description='\n            Define what to create.\n            ',
            definition=ObjectDefinition.TunableFactory(),
            recipe=RecipeDefinition.TunableFactory(),
            default='definition'),
        'initial_states':
        TunableList(
            description=
            '\n            A list of states to apply to the object as soon as it is created.\n            ',
            tunable=TunableStateValueReference()),
        'destroy_on_placement_failure':
        Tunable(
            description=
            '\n            If checked, the created object will be destroyed on placement failure.\n            If unchecked, the created object will be placed into an appropriate\n            inventory on placement failure if possible.  If THAT fails, object\n            will be destroyed.\n            ',
            tunable_type=bool,
            default=False),
        'cancel_on_destroy':
        Tunable(
            description=
            '\n            If checked, the interaction will be canceled if object is destroyed\n            due to placement failure or if destroy on placement failure is\n            unchecked and the fallback fails.\n            ',
            tunable_type=bool,
            default=True),
        'transient':
        Tunable(
            description=
            '\n            If checked, the created object will be destroyed when the interaction ends.\n            ',
            tunable_type=bool,
            default=False),
        'location':
        TunableVariant(
            description=
            '\n            Where the object should be created.\n            ',
            default='position',
            position=TunableTuple(
                description=
                '\n                An in-world position based off of the chosen Participant Type.\n                ',
                locked_args={'location': POSITION},
                location_target=TunableEnumEntry(
                    description=
                    '\n                    Who or what to create this object next to.\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.Actor),
                offset_tuning=TunableTuple(
                    default_offset=TunableVector3(
                        description=
                        "\n                        The default Vector3 offset from the location target's\n                        position.\n                        ",
                        default=sims4.math.Vector3.ZERO()),
                    x_randomization_range=OptionalTunable(
                        TunableInterval(
                            description=
                            '\n                        A random number in this range will be applied to the\n                        default offset along the x axis.\n                        ',
                            tunable_type=float,
                            default_lower=0,
                            default_upper=0)),
                    z_randomization_range=OptionalTunable(
                        TunableInterval(
                            description=
                            '\n                        A random number in this range will be applied to the\n                        default offset along the z axis.\n                        ',
                            tunable_type=float,
                            default_lower=0,
                            default_upper=0))),
                ignore_bb_footprints=Tunable(
                    description=
                    '\n                    Ignores the build buy object footprints when trying to find\n                    a position for creating this object.  This will allow \n                    objects to appear on top of each other.\n                    e.g. Trash cans when tipped over want to place the trash \n                    right under them so it looks like the pile came out from \n                    the object while it was tipped.\n                    ',
                    tunable_type=bool,
                    default=True),
                allow_off_lot_placement=Tunable(
                    description=
                    '\n                    If checked, objects will be allowed to be placed off-lot.\n                    If unchecked, we will always attempt to place created\n                    objects on the active lot.\n                    ',
                    tunable_type=bool,
                    default=False)),
            inventory=TunableTuple(
                description=
                '\n                An inventory based off of the chosen Participant Type.\n                ',
                locked_args={'location': INVENTORY},
                location_target=TunableEnumEntry(
                    description=
                    '\n                    "The owner of the inventory the object will be created in."\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.Actor)),
            slot=TunableTuple(
                description=
                '\n                Slot the object into the specified slot on the tuned location_target.\n                ',
                locked_args={'location': SLOT},
                location_target=TunableEnumEntry(
                    description=
                    '\n                    The object which will contain the specified slot.\n                    ',
                    tunable_type=ParticipantType,
                    default=ParticipantType.Object),
                parent_slot=TunableVariant(
                    description=
                    '\n                    The slot on location_target where the object should go. This\n                    may be either the exact name of a bone on the location_target or a\n                    slot type, in which case the first empty slot of the specified type\n                    in which the child object fits will be used.\n                    ',
                    by_name=Tunable(
                        description=
                        '\n                        The exact name of a slot on the location_target in which the target\n                        object should go.  \n                        ',
                        tunable_type=str,
                        default='_ctnm_'),
                    by_reference=TunableReference(
                        description=
                        '\n                        A particular slot type in which the target object should go.  The\n                        first empty slot of this type found on the location_target will be used.\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.SLOT_TYPE))))),
        'reserve_object':
        OptionalTunable(
            description=
            '\n            If this is enabled, the created object will be reserved for use by\n            the set Sim.\n            ',
            tunable=TunableEnumEntry(
                tunable_type=ParticipantTypeActorTargetSim,
                default=ParticipantTypeActorTargetSim.Actor))
    }

    def __init__(self, interaction, *args, sequence=(), **kwargs):
        super().__init__(interaction, sequence=sequence, *args, **kwargs)
        self._placement_failed = False
        if self.reserve_object is not None:
            reserved_sim = self.interaction.get_participant(
                self.reserve_object)
        else:
            reserved_sim = None
        self._object_helper = CreateObjectHelper(
            reserved_sim,
            self.definition,
            self,
            init=self._setup_created_object)

    @property
    def definition(self):
        return self.creation_data.get_definition()

    @property
    def placement_failed(self):
        return self._placement_failed

    def create_object(self):
        created_object = create_object(self.definition,
                                       init=self._setup_created_object,
                                       post_add=self._place_object)
        if self._placement_failed:
            created_object.destroy(
                source=self.interaction,
                cause='Failed to place object created by basic extra.')
            return
        return created_object

    def _build_outer_elements(self, sequence):
        return self._object_helper.create(sequence)

    def _do_behavior(self):
        self._place_object(self._object_helper.object)
        if self._placement_failed:
            if self.cancel_on_destroy:
                self.interaction.cancel(
                    FinishingType.FAILED_TESTS,
                    cancel_reason_msg='Cannot place object')
                return False
            return True
        if not self.transient:
            self._object_helper.claim()
        return True

    def _setup_created_object(self, created_object):
        self.creation_data.setup_created_object(self.interaction,
                                                created_object)
        for initial_state in self.initial_states:
            created_object.set_state(initial_state.state, initial_state)

    def _get_ignored_object_ids(self):
        pass

    def _place_object_no_fallback(self, created_object):
        participant = self.interaction.get_participant(
            self.location.location_target)
        if self.location.location == self.POSITION:
            offset_tuning = self.location.offset_tuning
            default_offset = sims4.math.Vector3(offset_tuning.default_offset.x,
                                                offset_tuning.default_offset.y,
                                                offset_tuning.default_offset.z)
            x_range = offset_tuning.x_randomization_range
            z_range = offset_tuning.z_randomization_range
            start_orientation = sims4.random.random_orientation()
            if x_range is not None:
                x_axis = start_orientation.transform_vector(
                    sims4.math.Vector3.X_AXIS())
                default_offset += x_axis * random.uniform(
                    x_range.lower_bound, x_range.upper_bound)
            if z_range is not None:
                z_axis = start_orientation.transform_vector(
                    sims4.math.Vector3.Z_AXIS())
                default_offset += z_axis * random.uniform(
                    z_range.lower_bound, z_range.upper_bound)
            offset = sims4.math.Transform(default_offset,
                                          sims4.math.Quaternion.IDENTITY())
            start_position = sims4.math.Transform.concatenate(
                offset, participant.transform).translation
            routing_surface = participant.routing_surface
            active_lot = services.active_lot()
            search_flags = placement.FGLSearchFlagsDefault
            if self.location.allow_off_lot_placement and not active_lot.is_position_on_lot(
                    start_position):
                created_object.location = sims4.math.Location(
                    sims4.math.Transform(start_position, start_orientation),
                    routing_surface)
                polygon = placement.get_accurate_placement_footprint_polygon(
                    created_object.position, created_object.orientation,
                    created_object.scale, created_object.get_footprint())
                context = placement.FindGoodLocationContext(
                    starting_position=start_position,
                    starting_orientation=start_orientation,
                    starting_routing_surface=routing_surface,
                    ignored_object_ids=(created_object.id, ),
                    search_flags=search_flags,
                    object_polygons=(polygon, ))
            else:
                if not self.location.ignore_bb_footprints:
                    search_flags |= placement.FGLSearchFlag.SHOULD_TEST_BUILDBUY | placement.FGLSearchFlag.STAY_IN_CURRENT_BLOCK
                    if not active_lot.is_position_on_lot(start_position):
                        start_position = active_lot.get_default_position(
                            position=start_position)
                context = placement.FindGoodLocationContext(
                    starting_position=start_position,
                    starting_orientation=start_orientation,
                    starting_routing_surface=routing_surface,
                    object_id=created_object.id,
                    ignored_object_ids=self._get_ignored_object_ids(),
                    search_flags=search_flags,
                    object_footprints=(self.definition.get_footprint(0), ))
            (translation, orientation) = placement.find_good_location(context)
            if translation is not None:
                created_object.move_to(routing_surface=routing_surface,
                                       translation=translation,
                                       orientation=orientation)
                return True
        elif self.location.location == self.SLOT:
            parent_slot = self.location.parent_slot
            if participant.slot_object(parent_slot=parent_slot,
                                       slotting_object=created_object):
                return True
        return False

    def _place_object(self, created_object):
        if self._place_object_no_fallback(created_object):
            return True
        if not self.destroy_on_placement_failure:
            participant = self.interaction.get_participant(
                self.location.location_target)
            if participant.inventory_component is not None and created_object.inventoryitem_component is not None:
                if participant.is_sim:
                    participant_household_id = participant.household.id
                else:
                    participant_household_id = participant.get_household_owner_id(
                    )
                created_object.set_household_owner_id(participant_household_id)
                participant.inventory_component.system_add_object(
                    created_object, participant)
                return True
            sim = self.interaction.sim
            if sim is not None:
                if not sim.household.is_npc_household:
                    try:
                        created_object.set_household_owner_id(sim.household.id)
                        build_buy.move_object_to_household_inventory(
                            created_object)
                        return True
                    except KeyError:
                        pass
        self._placement_failed = True
        return False
class CollectManyInteraction(SuperInteraction):
    __qualname__ = 'CollectManyInteraction'
    INTERACTION_TARGET = 'interaction_target'
    INSTANCE_TUNABLES = {'aggregate_object': TunableVariant(description='\n            The type of object to use as the aggregate object.  If a definition\n            is specified, the aggregate object will be created using that\n            definition.  If "interaction_target" is specified, the aggregate object\n            will be created using the definition of the interaction target.\n            ', definition=TunableReference(description='\n                A reference to the type of object that will be created as part\n                of this interaction to represent the many collected objects the\n                participant has picked up.\n                ', manager=services.definition_manager()), locked_args={'interaction_target': INTERACTION_TARGET}, default='definition'), 'destroy_original_object': Tunable(description="\n            If checked, the original object (the target of this interaction),\n            will be destroyed and replaced with the specified aggregate object.\n            If unchecked, the aggregate object will be created in the Sim's\n            hand, but the original object will not be destroyed.\n            ", tunable_type=bool, default=True)}
    DIRTY_DISH_ACTOR_NAME = 'dirtydish'
    ITEMS_PARAM = 'items'
    _object_create_helper = None
    _collected_targets = WeakSet()

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

    @property
    def create_object_owner(self):
        return self.sim

    @property
    def _aggregate_object_definition(self):
        if self.aggregate_object is self.INTERACTION_TARGET:
            return self.target.definition
        return self.aggregate_object

    @property
    def create_target(self):
        if self.context.carry_target is not None:
            return
        return self._aggregate_object_definition

    @property
    def created_target(self):
        return self.context.carry_target

    @classmethod
    def _test(cls, target, context, **interaction_parameters):
        if target is not None and target in cls._collected_targets:
            return TestResult(False, 'Target was already collected.')
        return super()._test(target, context, **interaction_parameters)

    def _setup_collected_object(self, obj):
        self.context.carry_target = obj

    def setup_asm_default(self, asm, *args, **kwargs):
        result = super().setup_asm_default(asm, *args, **kwargs)
        if self.target is not None:
            surface_height = get_surface_height_parameter_for_object(self.target)
            asm.set_parameter('surfaceHeight', surface_height)
            if self.target.parent is not None:
                asm.set_actor('surface', self.target.parent)
        if self._original_carry_target is not None:
            param_overrides = self._original_carry_target.get_param_overrides(self.DIRTY_DISH_ACTOR_NAME, only_for_keys=(self.ITEMS_PARAM,))
            if param_overrides is not None:
                asm.update_locked_params(param_overrides)
        return result

    def build_basic_content(self, sequence=(), **kwargs):
        self.animation_context.register_event_handler(self._xevt_callback, handler_id=SCRIPT_EVENT_ID_START_CARRY)
        if self._aggregate_object_definition is None or self.carry_target is not None and self._aggregate_object_definition is self.carry_target.definition:
            if self.context.carry_target is None:
                self._setup_collected_object(self.target)
            return super().build_basic_content(sequence, **kwargs)
        if self.carry_target is not None:
            swap_carry = True
            self._original_carry_target = self.carry_target
        else:
            swap_carry = False
        self._object_create_helper = CreateObjectHelper(self.sim, self._aggregate_object_definition.id, self, post_add=self._setup_collected_object, tag='Aggregate object created for a CollectManyInteraction.')
        super_build_basic_content = super().build_basic_content

        def grab_sequence(timeline):
            nonlocal sequence
            sequence = super_build_basic_content(sequence)
            if swap_carry:
                sequence = swap_carry_while_holding(self, self._original_carry_target, self.created_target, callback=self._object_create_helper.claim, sequence=sequence)
            else:
                sequence = enter_carry_while_holding(self, self.created_target, callback=self._object_create_helper.claim, create_si_fn=None, sequence=sequence)
            result = yield element_utils.run_child(timeline, sequence)

        return self._object_create_helper.create(grab_sequence)

    def _xevt_callback(self, *_, **__):
        if self.carry_target is not None and self.target is not None:
            if self._object_create_helper is None:
                for statistic in self.target.statistic_tracker:
                    self.carry_target.statistic_tracker.add_value(statistic.stat_type, statistic.get_value())
            elif self._original_carry_target is not None:
                for statistic in self._original_carry_target.statistic_tracker:
                    self.carry_target.statistic_tracker.add_value(statistic.stat_type, statistic.get_value())
            elif self.aggregate_object is self.INTERACTION_TARGET:
                self.carry_target.copy_state_values(self.target)
            else:
                for statistic in self.target.statistic_tracker:
                    self.carry_target.statistic_tracker.set_value(statistic.stat_type, statistic.get_value())
        if self.destroy_original_object and self.target is not None:
            self._collected_targets.add(self.target)
            self.target.transient = True
            self.target.remove_from_client()

    @classproperty
    def requires_target_support(cls):
        return False
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 CollectManyInteraction(SuperInteraction):
    __qualname__ = 'CollectManyInteraction'
    INTERACTION_TARGET = 'interaction_target'
    INSTANCE_TUNABLES = {
        'aggregate_object':
        TunableVariant(
            description=
            '\n            The type of object to use as the aggregate object.  If a definition\n            is specified, the aggregate object will be created using that\n            definition.  If "interaction_target" is specified, the aggregate object\n            will be created using the definition of the interaction target.\n            ',
            definition=TunableReference(
                description=
                '\n                A reference to the type of object that will be created as part\n                of this interaction to represent the many collected objects the\n                participant has picked up.\n                ',
                manager=services.definition_manager()),
            locked_args={'interaction_target': INTERACTION_TARGET},
            default='definition'),
        'destroy_original_object':
        Tunable(
            description=
            "\n            If checked, the original object (the target of this interaction),\n            will be destroyed and replaced with the specified aggregate object.\n            If unchecked, the aggregate object will be created in the Sim's\n            hand, but the original object will not be destroyed.\n            ",
            tunable_type=bool,
            default=True)
    }
    DIRTY_DISH_ACTOR_NAME = 'dirtydish'
    ITEMS_PARAM = 'items'
    _object_create_helper = None
    _collected_targets = WeakSet()

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

    @property
    def create_object_owner(self):
        return self.sim

    @property
    def _aggregate_object_definition(self):
        if self.aggregate_object is self.INTERACTION_TARGET:
            return self.target.definition
        return self.aggregate_object

    @property
    def create_target(self):
        if self.context.carry_target is not None:
            return
        return self._aggregate_object_definition

    @property
    def created_target(self):
        return self.context.carry_target

    @classmethod
    def _test(cls, target, context, **interaction_parameters):
        if target is not None and target in cls._collected_targets:
            return TestResult(False, 'Target was already collected.')
        return super()._test(target, context, **interaction_parameters)

    def _setup_collected_object(self, obj):
        self.context.carry_target = obj

    def setup_asm_default(self, asm, *args, **kwargs):
        result = super().setup_asm_default(asm, *args, **kwargs)
        if self.target is not None:
            surface_height = get_surface_height_parameter_for_object(
                self.target)
            asm.set_parameter('surfaceHeight', surface_height)
            if self.target.parent is not None:
                asm.set_actor('surface', self.target.parent)
        if self._original_carry_target is not None:
            param_overrides = self._original_carry_target.get_param_overrides(
                self.DIRTY_DISH_ACTOR_NAME, only_for_keys=(self.ITEMS_PARAM, ))
            if param_overrides is not None:
                asm.update_locked_params(param_overrides)
        return result

    def build_basic_content(self, sequence=(), **kwargs):
        self.animation_context.register_event_handler(
            self._xevt_callback, handler_id=SCRIPT_EVENT_ID_START_CARRY)
        if self._aggregate_object_definition is None or self.carry_target is not None and self._aggregate_object_definition is self.carry_target.definition:
            if self.context.carry_target is None:
                self._setup_collected_object(self.target)
            return super().build_basic_content(sequence, **kwargs)
        if self.carry_target is not None:
            swap_carry = True
            self._original_carry_target = self.carry_target
        else:
            swap_carry = False
        self._object_create_helper = CreateObjectHelper(
            self.sim,
            self._aggregate_object_definition.id,
            self,
            post_add=self._setup_collected_object,
            tag='Aggregate object created for a CollectManyInteraction.')
        super_build_basic_content = super().build_basic_content

        def grab_sequence(timeline):
            nonlocal sequence
            sequence = super_build_basic_content(sequence)
            if swap_carry:
                sequence = swap_carry_while_holding(
                    self,
                    self._original_carry_target,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    sequence=sequence)
            else:
                sequence = enter_carry_while_holding(
                    self,
                    self.created_target,
                    callback=self._object_create_helper.claim,
                    create_si_fn=None,
                    sequence=sequence)
            result = yield element_utils.run_child(timeline, sequence)

        return self._object_create_helper.create(grab_sequence)

    def _xevt_callback(self, *_, **__):
        if self.carry_target is not None and self.target is not None:
            if self._object_create_helper is None:
                for statistic in self.target.statistic_tracker:
                    self.carry_target.statistic_tracker.add_value(
                        statistic.stat_type, statistic.get_value())
            elif self._original_carry_target is not None:
                for statistic in self._original_carry_target.statistic_tracker:
                    self.carry_target.statistic_tracker.add_value(
                        statistic.stat_type, statistic.get_value())
            elif self.aggregate_object is self.INTERACTION_TARGET:
                self.carry_target.copy_state_values(self.target)
            else:
                for statistic in self.target.statistic_tracker:
                    self.carry_target.statistic_tracker.set_value(
                        statistic.stat_type, statistic.get_value())
        if self.destroy_original_object and self.target is not None:
            self._collected_targets.add(self.target)
            self.target.transient = True
            self.target.remove_from_client()

    @classproperty
    def requires_target_support(cls):
        return False
Example #17
0
class ObjectCreationElement(XevtTriggeredElement):
    __qualname__ = 'ObjectCreationElement'
    POSITION = 'position'
    INVENTORY = 'inventory'
    SLOT = 'slot'

    class ObjectDefinition(HasTunableSingletonFactory, AutoFactoryInit):
        __qualname__ = 'ObjectCreationElement.ObjectDefinition'
        FACTORY_TUNABLES = {'definition': TunableReference(description='\n                The definition of the object that is created.\n                ', manager=services.definition_manager())}

        def get_definition(self):
            return self.definition

        def setup_created_object(self, interaction, created_object):
            pass

    class RecipeDefinition(HasTunableSingletonFactory, AutoFactoryInit):
        __qualname__ = 'ObjectCreationElement.RecipeDefinition'
        FACTORY_TUNABLES = {'recipe': TunableReference(description='\n                The recipe to use to create the object.\n                ', manager=services.recipe_manager())}

        def get_definition(self):
            return self.recipe.final_product.definition

        def setup_created_object(self, interaction, created_object):
            from crafting.crafting_process import CraftingProcess
            crafting_process = CraftingProcess(crafter=interaction.sim, recipe=self.recipe)
            crafting_process.setup_crafted_object(created_object, is_final_product=True)

    FACTORY_TUNABLES = {'description': '\n            Create an object as part of an interaction.\n            ', 'creation_data': TunableVariant(description='\n            Define what to create.\n            ', definition=ObjectDefinition.TunableFactory(), recipe=RecipeDefinition.TunableFactory(), default='definition'), 'initial_states': TunableList(description='\n            A list of states to apply to the object as soon as it is created.\n            ', tunable=TunableStateValueReference()), 'destroy_on_placement_failure': Tunable(description='\n            If checked, the created object will be destroyed on placement failure.\n            If unchecked, the created object will be placed into an appropriate\n            inventory on placement failure if possible.  If THAT fails, object\n            will be destroyed.\n            ', tunable_type=bool, default=False), 'cancel_on_destroy': Tunable(description='\n            If checked, the interaction will be canceled if object is destroyed\n            due to placement failure or if destroy on placement failure is\n            unchecked and the fallback fails.\n            ', tunable_type=bool, default=True), 'transient': Tunable(description='\n            If checked, the created object will be destroyed when the interaction ends.\n            ', tunable_type=bool, default=False), 'location': TunableVariant(description='\n            Where the object should be created.\n            ', default='position', position=TunableTuple(description='\n                An in-world position based off of the chosen Participant Type.\n                ', locked_args={'location': POSITION}, location_target=TunableEnumEntry(description='\n                    Who or what to create this object next to.\n                    ', tunable_type=ParticipantType, default=ParticipantType.Actor), offset_tuning=TunableTuple(default_offset=TunableVector3(description="\n                        The default Vector3 offset from the location target's\n                        position.\n                        ", default=sims4.math.Vector3.ZERO()), x_randomization_range=OptionalTunable(TunableInterval(description='\n                        A random number in this range will be applied to the\n                        default offset along the x axis.\n                        ', tunable_type=float, default_lower=0, default_upper=0)), z_randomization_range=OptionalTunable(TunableInterval(description='\n                        A random number in this range will be applied to the\n                        default offset along the z axis.\n                        ', tunable_type=float, default_lower=0, default_upper=0))), ignore_bb_footprints=Tunable(description='\n                    Ignores the build buy object footprints when trying to find\n                    a position for creating this object.  This will allow \n                    objects to appear on top of each other.\n                    e.g. Trash cans when tipped over want to place the trash \n                    right under them so it looks like the pile came out from \n                    the object while it was tipped.\n                    ', tunable_type=bool, default=True), allow_off_lot_placement=Tunable(description='\n                    If checked, objects will be allowed to be placed off-lot.\n                    If unchecked, we will always attempt to place created\n                    objects on the active lot.\n                    ', tunable_type=bool, default=False)), inventory=TunableTuple(description='\n                An inventory based off of the chosen Participant Type.\n                ', locked_args={'location': INVENTORY}, location_target=TunableEnumEntry(description='\n                    "The owner of the inventory the object will be created in."\n                    ', tunable_type=ParticipantType, default=ParticipantType.Actor)), slot=TunableTuple(description='\n                Slot the object into the specified slot on the tuned location_target.\n                ', locked_args={'location': SLOT}, location_target=TunableEnumEntry(description='\n                    The object which will contain the specified slot.\n                    ', tunable_type=ParticipantType, default=ParticipantType.Object), parent_slot=TunableVariant(description='\n                    The slot on location_target where the object should go. This\n                    may be either the exact name of a bone on the location_target or a\n                    slot type, in which case the first empty slot of the specified type\n                    in which the child object fits will be used.\n                    ', by_name=Tunable(description='\n                        The exact name of a slot on the location_target in which the target\n                        object should go.  \n                        ', tunable_type=str, default='_ctnm_'), by_reference=TunableReference(description='\n                        A particular slot type in which the target object should go.  The\n                        first empty slot of this type found on the location_target will be used.\n                        ', manager=services.get_instance_manager(sims4.resources.Types.SLOT_TYPE))))), 'reserve_object': OptionalTunable(description='\n            If this is enabled, the created object will be reserved for use by\n            the set Sim.\n            ', tunable=TunableEnumEntry(tunable_type=ParticipantTypeActorTargetSim, default=ParticipantTypeActorTargetSim.Actor))}

    def __init__(self, interaction, *args, sequence=(), **kwargs):
        super().__init__(interaction, sequence=sequence, *args, **kwargs)
        self._placement_failed = False
        if self.reserve_object is not None:
            reserved_sim = self.interaction.get_participant(self.reserve_object)
        else:
            reserved_sim = None
        self._object_helper = CreateObjectHelper(reserved_sim, self.definition, self, init=self._setup_created_object)

    @property
    def definition(self):
        return self.creation_data.get_definition()

    @property
    def placement_failed(self):
        return self._placement_failed

    def create_object(self):
        created_object = create_object(self.definition, init=self._setup_created_object, post_add=self._place_object)
        if self._placement_failed:
            created_object.destroy(source=self.interaction, cause='Failed to place object created by basic extra.')
            return
        return created_object

    def _build_outer_elements(self, sequence):
        return self._object_helper.create(sequence)

    def _do_behavior(self):
        self._place_object(self._object_helper.object)
        if self._placement_failed:
            if self.cancel_on_destroy:
                self.interaction.cancel(FinishingType.FAILED_TESTS, cancel_reason_msg='Cannot place object')
                return False
            return True
        if not self.transient:
            self._object_helper.claim()
        return True

    def _setup_created_object(self, created_object):
        self.creation_data.setup_created_object(self.interaction, created_object)
        for initial_state in self.initial_states:
            created_object.set_state(initial_state.state, initial_state)

    def _get_ignored_object_ids(self):
        pass

    def _place_object_no_fallback(self, created_object):
        participant = self.interaction.get_participant(self.location.location_target)
        if self.location.location == self.POSITION:
            offset_tuning = self.location.offset_tuning
            default_offset = sims4.math.Vector3(offset_tuning.default_offset.x, offset_tuning.default_offset.y, offset_tuning.default_offset.z)
            x_range = offset_tuning.x_randomization_range
            z_range = offset_tuning.z_randomization_range
            start_orientation = sims4.random.random_orientation()
            if x_range is not None:
                x_axis = start_orientation.transform_vector(sims4.math.Vector3.X_AXIS())
                default_offset += x_axis*random.uniform(x_range.lower_bound, x_range.upper_bound)
            if z_range is not None:
                z_axis = start_orientation.transform_vector(sims4.math.Vector3.Z_AXIS())
                default_offset += z_axis*random.uniform(z_range.lower_bound, z_range.upper_bound)
            offset = sims4.math.Transform(default_offset, sims4.math.Quaternion.IDENTITY())
            start_position = sims4.math.Transform.concatenate(offset, participant.transform).translation
            routing_surface = participant.routing_surface
            active_lot = services.active_lot()
            search_flags = placement.FGLSearchFlagsDefault
            if self.location.allow_off_lot_placement and not active_lot.is_position_on_lot(start_position):
                created_object.location = sims4.math.Location(sims4.math.Transform(start_position, start_orientation), routing_surface)
                polygon = placement.get_accurate_placement_footprint_polygon(created_object.position, created_object.orientation, created_object.scale, created_object.get_footprint())
                context = placement.FindGoodLocationContext(starting_position=start_position, starting_orientation=start_orientation, starting_routing_surface=routing_surface, ignored_object_ids=(created_object.id,), search_flags=search_flags, object_polygons=(polygon,))
            else:
                if not self.location.ignore_bb_footprints:
                    search_flags |= placement.FGLSearchFlag.SHOULD_TEST_BUILDBUY | placement.FGLSearchFlag.STAY_IN_CURRENT_BLOCK
                    if not active_lot.is_position_on_lot(start_position):
                        start_position = active_lot.get_default_position(position=start_position)
                context = placement.FindGoodLocationContext(starting_position=start_position, starting_orientation=start_orientation, starting_routing_surface=routing_surface, object_id=created_object.id, ignored_object_ids=self._get_ignored_object_ids(), search_flags=search_flags, object_footprints=(self.definition.get_footprint(0),))
            (translation, orientation) = placement.find_good_location(context)
            if translation is not None:
                created_object.move_to(routing_surface=routing_surface, translation=translation, orientation=orientation)
                return True
        elif self.location.location == self.SLOT:
            parent_slot = self.location.parent_slot
            if participant.slot_object(parent_slot=parent_slot, slotting_object=created_object):
                return True
        return False

    def _place_object(self, created_object):
        if self._place_object_no_fallback(created_object):
            return True
        if not self.destroy_on_placement_failure:
            participant = self.interaction.get_participant(self.location.location_target)
            if participant.inventory_component is not None and created_object.inventoryitem_component is not None:
                if participant.is_sim:
                    participant_household_id = participant.household.id
                else:
                    participant_household_id = participant.get_household_owner_id()
                created_object.set_household_owner_id(participant_household_id)
                participant.inventory_component.system_add_object(created_object, participant)
                return True
            sim = self.interaction.sim
            if sim is not None:
                if not sim.household.is_npc_household:
                    try:
                        created_object.set_household_owner_id(sim.household.id)
                        build_buy.move_object_to_household_inventory(created_object)
                        return True
                    except KeyError:
                        pass
        self._placement_failed = True
        return False