Ejemplo n.º 1
0
class ConsumableComponent(Component, HasTunableFactory, AutoFactoryInit, component_name=types.CONSUMABLE_COMPONENT):
    manager = services.get_instance_manager(sims4.resources.Types.STATISTIC)
    CALORIES_PER_POUND = Tunable(int, 3500, description='Number of calories in 1 pound of fat.')
    SIM_WEIGHT_RANGE = Tunable(int, 100, description='The difference in pounds between Sims with empty and full fat commodities.')
    FAT_COMMODITY = TunableReference(manager, description="A reference to the Sim's fat commodity.")
    FIT_COMMODITY = TunableReference(manager, description="A reference to the Sim's fit commodity.")
    CONSUMABLE_COMMODITY = TunableReference(manager, description="A reference to the Object's consumable commodity.")
    FAT_STATE = TunableStateTypeReference(description='The fatness state type.')
    FIT_STATE = TunableStateTypeReference(description='The fit state type.')
    FACTORY_TUNABLES = {'consumption_turns': TunableRange(description='\n            An integer value specifying the number of turns it would take a Sim\n            to completely consume this object.\n            ', tunable_type=int, default=10, minimum=1), 'consumption_statistics': TunableList(description="\n            Statistic changes whose values represent the values that the\n            complete consumption of this object would provide.\n            \n            e.g. A statistic change of 50 for the hunger commodity will fill a\n            Sim's hunger commodity by 25 if they consume half of this object,\n            and by 50 if they consume all of it.\n            \n            The following commodities will have statistic changes automatically\n            generated based on other information and do not need to be added\n            explicitly:\n            \n             * Fat commodity\n             * Fit commodity\n             * Consumable commodity\n            ", tunable=TunableVariant(description='\n                The operation that defines the consumption statistic change.\n                ', statistic_change=StatisticChangeOp.TunableFactory(statistic_override=StatisticChangeOp.get_statistic_override(pack_safe=True), **StatisticOperation.DEFAULT_PARTICIPANT_ARGUMENTS), relationship_change=StatisticAddRelationship.TunableFactory(**RelationshipOperation.DEFAULT_PARTICIPANT_ARGUMENTS))), 'fitness_info': TunableTuple(description='\n            A list of tunables that affect Sim fitness.\n            ', calories=Tunable(description='\n                The number of calories contained in this consumable.\n                \n                If this object is marked as having a consumption effect, this\n                value will be used to generate appropriate fat gains or losses\n                for the Sim consuming this object.\n                ', tunable_type=int, default=500), consumption_effect=TunableEnumEntry(description='\n                The effect that consuming this object will have on the Sim.\n                ', tunable_type=ConsumptionEffects, default=ConsumptionEffects.NO_EFFECT)), 'consume_affordances': TunableList(description='\n            List of consume affordances that are forwarded to the consumable.\n            ', tunable=TunableReference(description="\n                The affordance that interfaces with this component and consumes the\n                owning object.  This affordance will be dynamically added to the\n                owning object's super affordance list at runtime.\n                ", manager=services.affordance_manager(), class_restrictions=('SuperInteraction',), pack_safe=True)), 'allow_destruction_on_inventory_transfer': Tunable(description="\n            If checked, this consumable is not going to survive attempts to\n            automatically be placed in a Sim's inventory. \n            \n            For instance, it would not survive a transfer from a Sim's inventory\n            to its household inventory upon death. Likewise, it would not\n            survive an automatic transfer from the world to a Sim's inventory\n            when its parent object is inventoried.\n            \n            Regular consumables, such as food, would probably want to leave this\n            checked. However, more meaningful consumables, such as Potions,\n            might want to prevent this behavior.\n            ", tunable_type=bool, default=True)}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.commodity_range = self.FAT_COMMODITY.max_value_tuning - self.FAT_COMMODITY.min_value_tuning
        self.calorie_modifier = self.CALORIES_PER_POUND*self.SIM_WEIGHT_RANGE/self.commodity_range
        self._loot_list = None

    @property
    def loot_list(self):
        if self._loot_list is None:
            self._derive_consumption_operations()
        return self._loot_list

    def component_super_affordances_gen(self, **kwargs):
        if self.consume_affordances is not None:
            yield from self.consume_affordances

    def _derive_consumption_operations(self):
        new_statistics = []
        for stat in self.consumption_statistics:
            amount = stat._amount/self.consumption_turns
            stat_change = StatisticChangeOp(amount=amount, stat=stat._stat, subject=stat._subject, tests=stat._tests)
            new_statistics.append(stat_change)
        if self.fitness_info.consumption_effect != ConsumptionEffects.NO_EFFECT:
            if self.fitness_info.consumption_effect == ConsumptionEffects.CALORIE_GAIN:
                amount = self.fitness_info.calories
            else:
                amount = -self.fitness_info.calories
            amount = amount/self.calorie_modifier
            amount /= self.consumption_turns
            stat_change = StatisticChangeOp(amount=amount, stat=self.FAT_COMMODITY, subject=ParticipantType.Actor)
            new_statistics.append(stat_change)
        if not debug_consumables_are_infinite:
            commodity_range = self.CONSUMABLE_COMMODITY.max_value_tuning - self.CONSUMABLE_COMMODITY.min_value_tuning
            amount = commodity_range/self.consumption_turns
            stat_change = StatisticChangeOp(amount=-amount, stat=self.CONSUMABLE_COMMODITY, subject=ParticipantType.Object)
            new_statistics.append(stat_change)
        loot_actions = LootActions(run_test_first=False, loot_actions=new_statistics)
        self._loot_list = [loot_actions]

    def bites_left(self):
        commodity_range = self.CONSUMABLE_COMMODITY.max_value_tuning - self.CONSUMABLE_COMMODITY.min_value_tuning
        amount_per_turn = commodity_range/self.consumption_turns
        current_value = self.owner.commodity_tracker.get_value(self.CONSUMABLE_COMMODITY)
        bites_left = current_value/amount_per_turn
        return bites_left
class TimeOfDayComponent(Component,
                         HasTunableFactory,
                         component_name=types.TIME_OF_DAY_COMPONENT):
    __qualname__ = 'TimeOfDayComponent'
    DAILY_REPEAT = date_and_time.create_time_span(hours=24)
    FACTORY_TUNABLES = {
        'state_changes':
        TunableMapping(
            description=
            '\n            A mapping from state to times of the day when the state should be \n            set to a tuned value.\n            ',
            key_type=TunableStateTypeReference(
                description='The state to be set.'),
            value_type=TunableList(
                description='List of times to modify the state at.',
                tunable=TunableTuple(start_time=TunableRange(
                    float,
                    0,
                    description=
                    'The start time (24 hour clock time) for the Day_Time state.',
                    minimum=0,
                    maximum=24),
                                     value=TunableStateValueReference(
                                         description='New state value.'))))
    }

    def __init__(self, owner, *, state_changes):
        super().__init__(owner)
        self.state_changes = state_changes
        self.alarm_handles = []

    def _add_alarm(self, cur_state, game_clock, state, change):
        time_to_day = game_clock.time_until_hour_of_day(change.start_time)

        def change_state(_):
            self.owner.set_state(state, change.value)

        self.alarm_handles.append(
            alarms.add_alarm(self.owner,
                             time_to_day,
                             change_state,
                             repeating=True,
                             repeating_time_span=self.DAILY_REPEAT))
        if cur_state is None or time_to_day > cur_state[0]:
            return (time_to_day, change.value)
        return cur_state

    def on_add(self):
        game_clock_service = services.game_clock_service()
        for (state, changes) in self.state_changes.items():
            current_state = None
            for change in changes:
                current_state = self._add_alarm(current_state,
                                                game_clock_service, state,
                                                change)
            while current_state is not None:
                self.owner.set_state(state, current_state[1])

    def on_remove(self):
        for handle in self.alarm_handles:
            alarms.cancel_alarm(handle)
Ejemplo n.º 3
0
class StereoComponent(Component,
                      HasTunableFactory,
                      AutoFactoryInit,
                      component_name=types.STEREO_COMPONENT):
    FACTORY_TUNABLES = {
        'channel_state':
        TunableStateTypeReference(
            description=
            '\n            The state used to populate the radio stations'),
        'off_state':
        TunableStateValueReference(
            description=
            '\n            The channel that represents the off state.'),
        'listen_affordances':
        TunableList(
            description=
            '\n            An ordered list of affordances that define "listening" to this\n            stereo. The first succeeding affordance is used.\n            ',
            tunable=TunableReference(manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION),
                                     pack_safe=True)),
        'play_on_active_sim_only':
        Tunable(
            description=
            '\n            If enabled, and audio target is Sim, the audio will only be \n            played on selected Sim. Otherwise it will be played regardless \n            Sim is selected or not.\n            \n            If audio target is Object, always set this to False. Otherwise\n            the audio will never be played.\n            \n            ex. This will be useful for Earbuds where we want to hear the\n            music only when the Sim is selected.\n            \n            This is passed down to the audio state when it is triggered, and thus\n            will overwrite any tuning on the state value.\n            ',
            tunable_type=bool,
            default=False),
        'immediate':
        Tunable(
            description=
            '\n            If checked, this audio will be triggered immediately, nothing\n            will block.\n            \n            ex. Earbuds audio will be played immediately while \n            the Sim is routing or animating.\n            \n            This is passed down to the audio state when it is triggered, and thus\n            will overwrite any tuning on the state value.\n            ',
            tunable_type=bool,
            default=False)
    }

    def is_stereo_turned_on(self):
        current_channel = self.owner.get_state(self.channel_state)
        return current_channel != self.off_state

    def get_available_picker_channel_states(self, context):
        for client_state in self.owner.get_client_states(self.channel_state):
            if client_state.show_in_picker:
                if client_state.test_channel(self.owner, context):
                    yield client_state

    def component_potential_interactions_gen(self, context, **kwargs):
        current_channel = self.owner.get_state(self.channel_state)
        if current_channel != self.off_state:
            for listen_affordance in self.listen_affordances:
                yield from listen_affordance.potential_interactions(
                    self.owner,
                    context,
                    required_station=current_channel,
                    off_state=self.off_state,
                    **kwargs)
Ejemplo n.º 4
0
class WatchCurrentChannelAutonomouslySuperInteraction(SuperInteraction):
    INSTANCE_TUNABLES = {
        'state':
        TunableStateTypeReference(
            description=
            '\n            The state to use to determine what to autonomously watch.\n            '
        )
    }

    def _run_interaction_gen(self, timeline):
        current_state = self.target.get_state(self.state)
        current_state.activate_channel(interaction=self, push_affordances=True)
        return True
        yield
Ejemplo n.º 5
0
class SkipToNextSongSuperInteraction(ImmediateSuperInteraction):
    __qualname__ = 'SkipToNextSongSuperInteraction'
    INSTANCE_TUNABLES = {'audio_state_type': TunableStateTypeReference(description='The state type that when changed, will change the audio on the target object. This is used to get the audio channel to advance the playlist.')}

    def _run_gen(self, timeline):
        play_audio_primative = self.target.get_component_managed_state_distributable('audio_state', self.affordance.audio_state_type)
        if play_audio_primative is not None:
            msg = Audio_pb2.SoundSkipToNext()
            msg.object_id = self.target.id
            msg.channel = play_audio_primative.channel
            distributor = Distributor.instance()
            distributor.add_op_with_no_owner(GenericProtocolBufferOp(Operation.OBJECT_AUDIO_PLAYLIST_SKIP_TO_NEXT, msg))
        return True
        yield None
Ejemplo n.º 6
0
class PickChannelAutonomouslySuperInteraction(AutonomousPickerSuperInteraction
                                              ):
    INSTANCE_TUNABLES = {
        'state':
        TunableStateTypeReference(
            description=
            '\n            The state used in the interaction.\n            '),
        'push_additional_affordances':
        Tunable(
            description=
            "\n            Whether to push affordances specified by the channel. This is used\n            for stereo's turn on and listen to... interaction.\n            ",
            tunable_type=bool,
            default=True)
    }

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

    @classmethod
    def _test(cls, target, context, **kwargs):
        test_result = super()._test(target, context, **kwargs)
        if not test_result:
            return test_result
        if not StatePickerEnumerationStrategy.has_valid_choice(
                target, context, state=cls.state):
            return TestResult(
                False, 'No valid choice in State Picker Enumeration Strategy.')
        return TestResult.TRUE

    def _run_interaction_gen(self, timeline):
        self._choice_enumeration_strategy.build_choice_list(self, self.state)
        chosen_state = self._choice_enumeration_strategy.find_best_choice(self)
        if chosen_state is None:
            logger.error(
                '{} fail to find a valid chosen state value for state {}'.
                format(self.__class__.__name__, self.state))
            return False
            yield
        chosen_state.activate_channel(
            interaction=self,
            push_affordances=self.push_additional_affordances)
        return True
        yield
Ejemplo n.º 7
0
class StoreObjectInfoLootOp(BaseTargetedLootOperation):
    FACTORY_TUNABLES = {
        'states_to_store':
        TunableSet(
            description=
            '\n            A list of states to be stored, if the source object has that state.\n            ',
            tunable=TunableStateTypeReference(
                description=
                '\n                A state to store.\n                ')),
        'stored_object_type':
        TunableEnumEntry(
            description=
            '\n            The type of object being stored. This will be used to retrieve the\n            stored object from the Stored Object Info Component of the target.\n            ',
            tunable_type=StoredObjectType,
            default=StoredObjectType.INVALID,
            invalid_enums=(StoredObjectType.INVALID, ))
    }

    def __init__(self, *args, states_to_store, stored_object_type, **kwargs):
        super().__init__(*args, **kwargs)
        self._states_to_store = states_to_store
        self._stored_object_type = stored_object_type

    def _get_state_guids_to_store(self, target):
        target_state_guids = set()
        for state in self._states_to_store:
            if target.has_state(state):
                target_state_value = target.get_state(state)
                target_state_guids.add(
                    (state.guid64, target_state_value.guid64))
        return target_state_guids

    def _apply_to_subject_and_target(self, subject, target, resolver):
        if not subject.has_component(types.STORED_OBJECT_INFO_COMPONENT):
            subject.add_dynamic_component(types.STORED_OBJECT_INFO_COMPONENT)
        custom_name = target.custom_name if target.has_custom_name() else None
        state_guids_to_store = self._get_state_guids_to_store(target)
        stored_object_component = subject.get_component(
            types.STORED_OBJECT_INFO_COMPONENT)
        stored_object_component.add_object(self._stored_object_type,
                                           target.id,
                                           obj_def_id=target.definition.id,
                                           custom_name=custom_name,
                                           state_guids=state_guids_to_store)
class StatePickerSuperInteraction(PickerSuperInteraction):
    INSTANCE_TUNABLES = {
        'state':
        TunableStateTypeReference(
            description=
            '\n            The state type used to populate the picker.\n            '
        )
    }

    @classmethod
    def _get_valid_state_values_gen(cls):
        for state_value in cls.state.values:
            if state_value.display_name is not None:
                yield state_value

    def on_choice_selected(self, state_value, **kwargs):
        if state_value is None:
            return
        self.target.set_state(self.state, state_value)

    @flexmethod
    def picker_rows_gen(cls, inst, target, context, **kwargs):
        inst_or_cls = inst if inst is not None else cls
        for state_value in inst_or_cls._get_valid_state_values_gen():
            if state_value._display_data is not None:
                state_name = state_value.display_name or LocalizationHelperTuning.get_raw_text(
                    state_value.__name__)
                row_tooltip = None if state_value.display_description is None else lambda *_, tooltip=state_value.display_description: tooltip
                yield ObjectPickerRow(
                    name=state_name,
                    row_description=state_value.display_description,
                    icon=state_value.display_icon,
                    tag=state_value,
                    row_tooltip=row_tooltip)

    def _run_interaction_gen(self, timeline):
        self._show_picker_dialog(self.sim)
        return True
        yield
Ejemplo n.º 9
0
class CurfewComponent(Component,
                      HasTunableFactory,
                      AutoFactoryInit,
                      component_name=CURFEW_COMPONENT):
    FACTORY_TUNABLES = {
        'curfew_state_reference':
        TunableStateTypeReference(
            description=
            '\n            This is a reference to the State type we will be manipulating when\n            we change states on this object.\n            '
        ),
        'times_state_reference':
        TunableStateTypeReference(
            description=
            '\n            This is a reference to the State type we will be manipulating when\n            we change states on this object.\n            '
        ),
        'curfew_not_set':
        TunableStateValueReference(
            description=
            '\n            This is the reference to the state to apply on the owning object\n            when there is no active curfew setting. Or the setting is UNSET.\n            '
        ),
        'curfew_warning_state':
        TunableStateValueReference(
            description=
            '\n            This is the reference to the state to apply to the owning object\n            when the curfew is about to start.\n            '
        ),
        'curfew_past_state':
        TunableStateValueReference(
            description=
            '\n            This is the reference to the state to apply to the owning object\n            when curfew is active.\n            '
        ),
        'curfew_on_state':
        TunableStateValueReference(
            description=
            '\n            This is the reference to the state to apply to the owning object\n            when the curfew is set but not currently active.\n            '
        ),
        'not_set_state':
        TunableStateValueReference(
            description=
            "\n            This is the reference to the state to apply to the owning object\n            when there isn't a curfew set at all.\n            "
        ),
        'times_set':
        TunableMapping(
            description=
            '\n            This is a Mapping of time (in military time) to state to apply to\n            the owning object in order to display the correct time that the\n            curfew is set for.\n            ',
            key_type=int,
            value_type=TunableStateValueReference()),
        'set_curfew_affordances':
        TunableList(
            description=
            '\n            A List of the interactions that will be used to set the curfew\n            via this object.\n            ',
            tunable=TunableReference(
                description=
                '\n              This is the interaction that will be used to "set" the curfew.\n                ',
                manager=services.affordance_manager(),
                class_restrictions=('SuperInteraction', )))
    }

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

    def on_add(self):
        curfew_service = services.get_curfew_service()
        current_curfew = curfew_service.get_zone_curfew(
            services.current_zone_id())
        if current_curfew not in self.times_set:
            pass
        self.apply_state_for_setting(current_curfew)
        self.apply_warning_state(current_curfew)
        self._register_for_alarms(curfew_service)

    def on_remove(self):
        curfew_service = services.get_curfew_service()
        self._unregister_for_alarms(curfew_service)

    def component_super_affordances_gen(self, **kwargs):
        yield from self.set_curfew_affordances

    def update_states(self, curfew_setting):
        if curfew_setting == self._current_curfew_setting:
            return
        self.apply_state_for_setting(curfew_setting)
        self.apply_warning_state(curfew_setting)

    def apply_state_for_setting(self, setting):
        if setting is CurfewService.UNSET:
            self.owner.set_state(self.times_state_reference,
                                 self.not_set_state)
        state_to_apply = self.times_set.get(setting)
        if state_to_apply is not None:
            self.owner.set_state(self.times_state_reference, state_to_apply)

    def apply_warning_state(self, curfew_setting):
        if curfew_setting is CurfewService.UNSET:
            self.owner.set_state(self.curfew_state_reference,
                                 self.curfew_not_set)
            return
        now = services.time_service().sim_now.hour()
        if now >= CurfewService.CURFEW_END_TIME and now < curfew_setting:
            self._on_curfew_over_alarm()
        elif now == curfew_setting - 1:
            self._on_warning_time_alarm()
        else:
            self._on_curfew_started_alarm()

    def _register_for_alarms(self, curfew_service):
        curfew_service.register_for_alarm_callbacks(
            self._on_warning_time_alarm, self._on_curfew_started_alarm,
            self._on_curfew_over_alarm, self.update_states)

    def _unregister_for_alarms(self, curfew_service):
        curfew_service.unregister_for_alarm_callbacks(
            self._on_warning_time_alarm, self._on_curfew_started_alarm,
            self._on_curfew_over_alarm, self.update_states)

    def _on_warning_time_alarm(self):
        self.owner.set_state(self.curfew_state_reference,
                             self.curfew_warning_state,
                             force_update=True)

    def _on_curfew_started_alarm(self):
        self.owner.set_state(self.curfew_state_reference,
                             self.curfew_past_state,
                             force_update=True)

    def _on_curfew_over_alarm(self):
        self.owner.set_state(self.curfew_state_reference,
                             self.curfew_on_state,
                             force_update=True)
Ejemplo n.º 10
0
class LaundryTuning:
    GENERATE_CLOTHING_PILE = TunableTuple(description='\n        The tunable to generate clothing pile on the lot. This will be called\n        when we find laundry hero objects on the lot and there is no hamper\n        available.\n        ', loot_to_apply=TunableReference(description='\n            Loot to apply for generating clothing pile.\n            ', manager=services.get_instance_manager(sims4.resources.Types.ACTION), class_restrictions=('LootActions',), pack_safe=True), naked_outfit_category=TunableSet(description="\n            Set of outfits categories which is considered naked.\n            When Sim switches FROM these outfits, it won't generate the pile.\n            When Sim switches TO these outfits, it won't apply laundry reward\n            or punishment.\n            ", tunable=TunableEnumEntry(tunable_type=OutfitCategory, default=OutfitCategory.EVERYDAY, invalid_enums=(OutfitCategory.CURRENT_OUTFIT,))), no_pile_outfit_category=TunableSet(description="\n            Set of outfits categories which will never generate the pile.\n            When Sim switches FROM or TO these outfits, it won't generate the\n            pile.\n            \n            Laundry reward or punishment will still be applied to the Sim when \n            switching FROM or TO these outfits.\n            ", tunable=TunableEnumEntry(tunable_type=OutfitCategory, default=OutfitCategory.EVERYDAY, invalid_enums=(OutfitCategory.CURRENT_OUTFIT,))), no_pile_interaction_tag=TunableEnumWithFilter(description='\n            If interaction does spin clothing change and has this tag, it will\n            generate no clothing pile.\n            ', tunable_type=Tag, default=Tag.INVALID, filter_prefixes=('interaction',)))
    HAMPER_OBJECT_TAGS = TunableTags(description='\n        Tags that considered hamper objects.\n        ', filter_prefixes=('func',))
    LAUNDRY_HERO_OBJECT_TAGS = TunableTags(description='\n        Tags of laundry hero objects. Placing any of these objects on the lot\n        will cause the service to generate clothing pile for each Sims on the\n        household after spin clothing change.\n        ', filter_prefixes=('func',))
    NOT_DOING_LAUNDRY_PUNISHMENT = TunableTuple(description='\n        If no Sim in the household unload completed laundry in specific\n        amount of time, the negative loot will be applied to Sim household \n        on spin clothing change to engage them doing laundry.\n        ', timeout=TunableSimMinute(description="\n            The amount of time in Sim minutes, since the last time they're \n            finishing laundry, before applying the loot.\n            ", default=2880, minimum=1), loot_to_apply=TunableReference(description='\n            Loot defined here will be applied to the Sim in the household\n            on spin clothing change if they are not doing laundry for \n            a while.\n            ', manager=services.get_instance_manager(sims4.resources.Types.ACTION), class_restrictions=('LootActions',), pack_safe=True))
    PUT_AWAY_FINISHED_LAUNDRY = TunableTuple(description='\n        The tunable to update laundry service on Put Away finished laundry\n        interaction.\n        ', interaction_tag=TunableEnumWithFilter(description='\n            Tag that represent the put away finished laundry interaction which \n            will update Laundry Service data.\n            ', tunable_type=Tag, default=Tag.INVALID, filter_prefixes=('interaction',)), laundry_condition_states=TunableTuple(description='\n            This is the state type of completed laundry object condition \n            which will aggregate the data to the laundry service.\n            ', condition_states=TunableList(description='\n                A list of state types to be stored on laundry service.\n                ', tunable=TunableStateTypeReference(pack_safe=True), unique_entries=True), excluded_states=TunableList(description='\n                A list of state values of Condition States which will not \n                be added to the laundry service.\n                ', tunable=TunableStateValueReference(pack_safe=True), unique_entries=True)), laundry_condition_timeout=TunableSimMinute(description='\n            The amount of time in Sim minutes that the individual laundry\n            finished conditions will be kept in the laundry conditions \n            aggregate data.\n            ', default=1440, minimum=0), conditions_and_rewards_map=TunableMapping(description='\n            Mapping of laundry conditions and loot rewards.\n            ', key_type=TunableReference(manager=services.get_instance_manager(sims4.resources.Types.OBJECT_STATE), pack_safe=True), value_type=TunableReference(manager=services.get_instance_manager(sims4.resources.Types.ACTION), class_restrictions=('LootActions',), pack_safe=True)))
    PUT_CLOTHING_PILE_ON_HAMPER = TunableTuple(description='\n        The Tunable to directly put generated clothing pile in the hamper.\n        ', chance=TunablePercent(description='\n            The chance that a clothing pile will be put directly in the hamper. \n            Tune the value in case putting clothing pile in hamper every \n            spin-outfit-change feeling excessive.\n            ', default=100), clothing_pile=TunableTuple(description="\n            Clothing pile object that will be created and put into the hamper \n            automatically. \n            \n            You won't see the object on the lot since it will go directly to \n            the hamper. We create it because we need to transfer all of the \n            commodities data and average the values into the hamper precisely.\n            ", definition=TunablePackSafeReference(description='\n                Reference to clothing pile object definition.\n                ', manager=services.definition_manager()), initial_states=TunableList(description='\n                A list of states to apply to the clothing pile as soon as it \n                is created.\n                ', tunable=TunableTuple(description='\n                    The state to apply and optional to decide if the state \n                    should be applied.\n                    ', state=TunableStateValueReference(pack_safe=True), tests=TunableTestSet()))), full_hamper_state=TunableStateValueReference(description='\n            The state of full hamper which make the hamper is unavailable to \n            add new clothing pile in it.\n            ', pack_safe=True), loots_to_apply=TunableList(description='\n            Loots to apply to the hamper when clothing pile is being put.\n            ', tunable=TunableReference(manager=services.get_instance_manager(sims4.resources.Types.ACTION), class_restrictions=('LootActions',), pack_safe=True)), tests=TunableTestSet(description='\n            The test to run on the Sim that must pass in order for putting\n            clothing pile automatically to the hamper. These tests will only \n            be run when we have available hamper on the lot.\n            '))
Ejemplo n.º 11
0
class TimeOfDayComponent(Component,
                         HasTunableFactory,
                         component_name=types.TIME_OF_DAY_COMPONENT):
    DAILY_REPEAT = date_and_time.create_time_span(hours=24)
    FACTORY_TUNABLES = {
        'state_changes':
        TunableMapping(
            description=
            '\n            A mapping from state to times of the day when the state should be \n            set to a tuned value.\n            ',
            key_type=TunableStateTypeReference(
                description=
                '\n                The state to be set.\n                '),
            value_type=TunableList(
                description=
                '\n                List of times to modify the state at.\n                ',
                tunable=TunableTuple(
                    start_time=TunableRange(
                        description=
                        '\n                        The start time (24 hour clock time) for the Day_Time state.\n                        ',
                        tunable_type=float,
                        default=0,
                        minimum=0,
                        maximum=24),
                    value=TunableStateValueReference(
                        description=
                        '\n                        New state value.\n                        '
                    ),
                    loot_list=TunableList(
                        description=
                        '\n                        A list of loot operations to apply when changing state.\n                        ',
                        tunable=TunableReference(
                            manager=services.get_instance_manager(
                                sims4.resources.Types.ACTION),
                            class_restrictions=('LootActions', ),
                            pack_safe=True)),
                    chance=SuccessChance.TunableFactory(
                        description=
                        '\n                        Percent chance that the state change will be considered. \n                        The chance is evaluated just before running the tests.\n                        '
                    ),
                    tests=TunableTestSet(
                        description=
                        '\n                        Test to decide whether the state change can be applied.\n                        '
                    ))))
    }

    def __init__(self, owner, *, state_changes):
        super().__init__(owner)
        self.state_changes = state_changes
        self.alarm_handles = []

    def _apply_state_change(self, state, change):
        resolver = SingleObjectResolver(self.owner)
        chance = change.chance.get_chance(resolver)
        if random.random() > chance:
            return
        if not change.tests.run_tests(resolver):
            return
        self.owner.set_state(state, change.value)
        for loot_action in change.loot_list:
            loot_action.apply_to_resolver(resolver)

    def _add_alarm(self, cur_state, state, change):
        now = services.time_service().sim_now
        time_to_day = clock.time_until_hour_of_day(now, change.start_time)

        def alarm_callback(_):
            self._apply_state_change(state, change)

        self.alarm_handles.append(
            alarms.add_alarm(self.owner,
                             time_to_day,
                             alarm_callback,
                             repeating=True,
                             repeating_time_span=self.DAILY_REPEAT))
        if cur_state is None or time_to_day > cur_state[0]:
            return (time_to_day, change)
        return cur_state

    def on_add(self):
        for (state, changes) in self.state_changes.items():
            current_state = None
            for change in changes:
                current_state = self._add_alarm(current_state, state, change)
            if current_state is not None:
                self._apply_state_change(state, current_state[1])

    def on_remove(self):
        for handle in self.alarm_handles:
            alarms.cancel_alarm(handle)
Ejemplo n.º 12
0
class ConsumableComponent(Component,
                          HasTunableFactory,
                          component_name=types.CONSUMABLE_COMPONENT):
    __qualname__ = 'ConsumableComponent'
    manager = services.get_instance_manager(sims4.resources.Types.STATISTIC)
    CALORIES_PER_POUND = Tunable(
        int, 3500, description='Number of calories in 1 pound of fat.')
    SIM_WEIGHT_RANGE = Tunable(
        int,
        100,
        description=
        'The difference in pounds between Sims with empty and full fat commodities.'
    )
    FAT_COMMODITY = TunableReference(
        manager, description="A reference to the Sim's fat commodity.")
    FIT_COMMODITY = TunableReference(
        manager, description="A reference to the Sim's fit commodity.")
    CONSUMABLE_COMMODITY = TunableReference(
        manager,
        description="A reference to the Object's consumable commodity.")
    FAT_STATE = TunableStateTypeReference(
        description='The fatness state type.')
    FIT_STATE = TunableStateTypeReference(description='The fit state type.')

    @staticmethod
    def _verify_tunable_callback(instance_class,
                                 tunable_name,
                                 source,
                                 consume_affordance=None,
                                 **tuned_values):
        if consume_affordance is None:
            logger.error(
                '{} has consumable component but has no consume affordance tuned',
                instance_class)

    FACTORY_TUNABLES = {
        'description':
        'Manage statistic changes for consumable objects.',
        'consumption_turns':
        TunableRange(
            description=
            '\n            An integer value specifying the number of turns it\n            would take a Sim to completely consume this\n            object.\n            ',
            tunable_type=int,
            default=10,
            minimum=1),
        'consumption_statistics':
        TunableList(
            description=
            "\n            Statistic changes whose values represent the values that the\n            complete consumption of this object would provide.  \n            \n            e.g. A statistic change of 50 for the hunger commodity will\n            fill a Sim's hunger commodity by 25 if they consume half of\n            this object, and by 50 if they consume all of it.\n            \n            The following commodities will have statistic changes\n            automatically generated based on other information and do not\n            need to be added explicitly:\n            \n             * Fat commodity\n             * Fit commodity\n             * Consumable commodity\n            ",
            tunable=TunableVariant(
                statistic_change=StatisticChangeOp.TunableFactory(
                    **StatisticOperation.DEFAULT_PARTICIPANT_ARGUMENTS),
                relationship_change=StatisticAddRelationship.TunableFactory(
                    **RelationshipOperation.DEFAULT_PARTICIPANT_ARGUMENTS))),
        'fitness_info':
        TunableTuple(
            description=
            '\n            A list of tunables that affect Sim fitness.\n            ',
            calories=Tunable(
                description=
                '\n                The number of calories contained in this consumable.  \n                \n                If this object is marked as having a consumption effect,\n                this value will be used to generate appropriate fat gains\n                or losses for the Sim consuming this object.\n                ',
                tunable_type=int,
                default=500),
            consumption_effect=TunableEnumEntry(
                description=
                '\n                The effect that consuming this object will have on the Sim.\n                ',
                tunable_type=ConsumptionEffects,
                needs_tuning=True,
                default=ConsumptionEffects.NO_EFFECT)),
        'consume_affordance':
        TunableReference(
            description=
            "\n            The affordance that interfaces with this component and consumes the\n            owning object.  This affordance will be dynamically added to the\n            owning object's super affordance list at runtime.\n            ",
            manager=services.affordance_manager(),
            class_restrictions=('SuperInteraction', )),
        'verify_tunable_callback':
        _verify_tunable_callback
    }

    def __init__(self, owner, consumption_turns, consumption_statistics,
                 fitness_info, consume_affordance, **kwargs):
        super().__init__(owner)
        self.owner = owner
        self.consumption_turns = consumption_turns
        self.consumption_statistics = list(consumption_statistics)
        self.fitness_info = fitness_info
        self.consume_affordance = consume_affordance
        self.commodity_range = self.FAT_COMMODITY.max_value_tuning - self.FAT_COMMODITY.min_value_tuning
        self.calorie_modifier = self.CALORIES_PER_POUND * self.SIM_WEIGHT_RANGE / self.commodity_range
        self._loot_list = None

    @property
    def loot_list(self):
        if self._loot_list is None:
            self._derive_consumption_operations()
        return self._loot_list

    def component_super_affordances_gen(self, **kwargs):
        if self.consume_affordance is not None:
            yield self.consume_affordance

    def _derive_consumption_operations(self):
        new_statistics = []
        for stat in self.consumption_statistics:
            amount = stat._amount / self.consumption_turns
            stat_change = StatisticChangeOp(amount=amount,
                                            stat=stat._stat,
                                            subject=stat._subject,
                                            tests=stat._tests)
            new_statistics.append(stat_change)
        if self.fitness_info.consumption_effect != ConsumptionEffects.NO_EFFECT:
            if self.fitness_info.consumption_effect == ConsumptionEffects.CALORIE_GAIN:
                amount = self.fitness_info.calories
            else:
                amount = -self.fitness_info.calories
            amount = amount / self.calorie_modifier
            amount /= self.consumption_turns
            stat_change = StatisticChangeOp(amount=amount,
                                            stat=self.FAT_COMMODITY,
                                            subject=ParticipantType.Actor)
            new_statistics.append(stat_change)
        if not debug_consumables_are_infinite:
            commodity_range = self.CONSUMABLE_COMMODITY.max_value_tuning - self.CONSUMABLE_COMMODITY.min_value_tuning
            amount = commodity_range / self.consumption_turns
            stat_change = StatisticChangeOp(amount=-amount,
                                            stat=self.CONSUMABLE_COMMODITY,
                                            subject=ParticipantType.Object)
            new_statistics.append(stat_change)
        loot_actions = LootActions(run_test_first=False,
                                   loot_actions=new_statistics)
        self._loot_list = [loot_actions]

    def bites_left(self):
        commodity_range = self.CONSUMABLE_COMMODITY.max_value_tuning - self.CONSUMABLE_COMMODITY.min_value_tuning
        amount_per_turn = commodity_range / self.consumption_turns
        current_value = self.owner.commodity_tracker.get_value(
            self.CONSUMABLE_COMMODITY)
        bites_left = current_value / amount_per_turn
        return bites_left