Exemple #1
0
 def __init__(self, **kwargs):
     super().__init__(
         description='The text for this slide.',
         text=TunableLocalizedString(),
         image=TunableResourceKey(
             description=
             '\n                             The image for this slide.\n                             ',
             default=None,
             resource_types=sims4.resources.CompoundTypes.IMAGE),
         platform_filter=TunableEnumEntry(
             description=
             '\n                            The platforms on which this slide is shown.\n                            ',
             tunable_type=TutorialPlatformFilter,
             default=TutorialPlatformFilter.ALL_PLATFORMS,
             export_modes=ExportModes.ClientBinary),
         image_console=TunableResourceKey(
             description=
             '\n                             The image for this slide on console.  If unset the Image will be used as a fallback.\n                             ',
             default=None,
             allow_none=True,
             resource_types=sims4.resources.CompoundTypes.IMAGE,
             display_name='Image (Console)',
             export_modes=ExportModes.ClientBinary),
         image_console_jp=TunableResourceKey(
             description=
             '\n                             The image for this slide on console for the JP SKU.  Fallback order is: Image (Console), Image.\n                             ',
             default=None,
             allow_none=True,
             resource_types=sims4.resources.CompoundTypes.IMAGE,
             display_name='Image (Console; JP)',
             export_modes=ExportModes.ClientBinary),
         **kwargs)
Exemple #2
0
 def __init__(self, **kwargs):
     super().__init__(
         cancelable=Tunable(
             description=
             '\n                If this tutorial tip can be canceled.\n                ',
             tunable_type=bool,
             default=True),
         text=TunableLocalizedStringFactory(
             description=
             "\n                The text for this tip.\n                Token {0} is the active sim. i.e. {0.SimFirstName}\n                Token {1.String} is a 'wildcard' string to be used for things\n                like aspiration names or buff names during the tutorial.\n                Not used when display type is INDICATOR_ARROW.\n                ",
             allow_none=True),
         action_text=TunableLocalizedStringFactory(
             description=
             "\n                The action the user must make for this tip to satisfy.\n                Token {0} is the active sim. i.e. {0.SimFirstName}\n                Token {1.String} is a 'wildcard' string to be used for things\n                like aspiration names or buff names during the tutorial.\n                ",
             allow_none=True),
         timeout=TunableRange(
             description=
             '\n                How long, in seconds, until this tutorial tip times out.\n                ',
             tunable_type=int,
             default=1,
             minimum=1),
         ui_element=TunableEnumEntry(
             description=
             '\n                The UI element associated with this tutorial tip.\n                ',
             tunable_type=TutorialTipUiElement,
             default=TutorialTipUiElement.UI_INVALID),
         is_modal=Tunable(
             description=
             '\n                Enable if this tip should be modal.\n                Disable, if not.\n                ',
             tunable_type=bool,
             default=False),
         icon=TunableResourceKey(
             description=
             '\n                The icon to be displayed in a modal tutorial tip.\n                If Is Modal is disabled, this field can be ignored.\n                ',
             resource_types=sims4.resources.CompoundTypes.IMAGE,
             default=None,
             allow_none=True),
         icon_console=TunableResourceKey(
             description=
             '\n                The icon to be displayed in a modal tutorial tip on console.\n                If unset, will fall back to Icon.\n                If Is Modal is disabled, this field can be ignored.\n                ',
             resource_types=sims4.resources.CompoundTypes.IMAGE,
             default=None,
             allow_none=True,
             display_name='Icon (Console)',
             export_modes=ExportModes.ClientBinary),
         title=TunableLocalizedString(
             description=
             '\n                The title of this tutorial tip.\n                Not used when display type is INDICATOR_ARROW.\n                ',
             allow_none=True),
         pagination_label=TunableLocalizedString(
             description=
             '\n                The label of what page this tutorial tip is in within the\n                tutorial tip group.\n                Not used when display type is INDICATOR_ARROW.\n                ',
             allow_none=True),
         display_type_option=TunableEnumEntry(
             description=
             '\n                The display type of this tutorial tip.\n                ',
             tunable_type=TutorialTipDisplayOption,
             default=TutorialTipDisplayOption.STANDARD),
         **kwargs)
Exemple #3
0
class MusicTrackData(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {
        'looping_audio':
        TunableResourceKey(
            description=
            '\n            The looping propx file of the music track.\n            ',
            resource_types=(Types.PROPX, )),
        'fixed_length_audio':
        TunableResourceKey(
            description=
            '\n            The fixed-length propx file of the music track.\n            ',
            resource_types=(Types.PROPX, ))
    }
Exemple #4
0
 def __init__(self, **kwargs):
     super().__init__(
         audio=TunableResourceKey(
             description=
             '\n                The sound to play.\n                ',
             default=None,
             resource_types=(sims4.resources.Types.PROPX, )),
         joint_name_hash=OptionalTunable(
             description=
             "\n                Specify if the audio is attached to a slot and, if so, which\n                slot. Otherwise the audio will be attached to the object's \n                origin.\n                ",
             tunable=TunableStringHash32(
                 description=
                 '\n                    The name of the slot this audio is attached to.\n                    '
             )),
         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                ',
             tunable_type=bool,
             default=False),
         immediate_audio=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                ',
             tunable_type=bool,
             default=False),
         **kwargs)
class TestableDisplayName(HasTunableSingletonFactory, AutoFactoryInit):
    __qualname__ = 'TestableDisplayName'

    @staticmethod
    def _verify_tunable_callback(instance_class, tunable_name, source, value):
        for (index, override_data) in enumerate(value.overrides):
            while not override_data.new_display_name:
                logger.error(
                    'name override not set for display name override in {} at index:{}',
                    instance_class, index)

    FACTORY_TUNABLES = {
        'overrides':
        TunableList(
            description=
            '\n            Potential name overrides for this interaction. The first test in\n            this list which passes will be the new display name show to the\n            player. If none pass the tuned display_name will be used.\n            ',
            tunable=TunableTuple(
                description=
                '\n                A tuple of a test and the name that would be chosen if the test\n                passes.\n                ',
                test=event_testing.tests.TunableTestSet(
                    description=
                    '\n                    The test to run to see if the display_name should be\n                    overridden.\n                    '
                ),
                new_display_name=TunableLocalizedStringFactory(
                    description=
                    '\n                    The localized name of this interaction. it takes two tokens,\n                    the actor (0) and target object (1) of the interaction.\n                    '
                ),
                new_pie_menu_icon=TunableResourceKey(
                    description=
                    '\n                    If this display name overrides the default display name,\n                    this will be the icon that is shown. If this is not tuned\n                    then the default pie menu icon for this interaction will be\n                    used.\n                    ',
                    default=None,
                    resource_types=sims4.resources.CompoundTypes.IMAGE),
                new_display_tooltip=OptionalTunable(
                    description=
                    '\n                    Tooltip to show on this pie menu option.\n                    ',
                    tunable=sims4.localization.TunableLocalizedStringFactory(
                    )),
                new_pie_menu_category=TunableReference(
                    description=
                    '\n                    Pie menu category to put interaction under.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.PIE_MENU_CATEGORY)))),
        'verify_tunable_callback':
        _verify_tunable_callback
    }

    def get_display_names_gen(self):
        for override in self.overrides:
            yield override.new_display_name

    def get_display_name_and_result(self,
                                    interaction,
                                    target=DEFAULT,
                                    context=DEFAULT):
        resolver = interaction.get_resolver(target=target, context=context)
        for override in self.overrides:
            result = override.test.run_tests(resolver)
            while result:
                return (override, result)
        return (None, TestResult.NONE)
Exemple #6
0
 def __init__(self, description='', **kwargs):
     super().__init__(
         image_large=TunableResourceKey(
             description=
             '\n                The large version of the screenshot displayed in the Pack\n                Preview Panel.\n                ',
             resource_types=sims4.resources.CompoundTypes.IMAGE),
         image_small=TunableResourceKey(
             description=
             '\n                The small version of the screenshot displayed in the Pack \n                Detail Panel.\n                ',
             resource_types=sims4.resources.CompoundTypes.IMAGE),
         title=TunableLocalizedString(
             description=
             '\n                The title displayed over the screenshot in both the Pack\n                Detail Panel and Pack Preview Panel.\n                '
         ),
         description=description,
         **kwargs)
Exemple #7
0
class PieMenuActions(enum.Int, export=False):
    __qualname__ = 'PieMenuActions'
    SHOW_PIE_MENU = 0
    SHOW_DEBUG_PIE_MENU = 1
    INTERACTION_QUEUE_FULL_TOOLTIP = 2
    INTERACTION_QUEUE_FULL_STR = TunableLocalizedStringFactory(description="\n        Tooltip string shown to the user instead of a pie menu when the Sim's queue\n        is full of interactions.\n        ")
    POSTURE_INCOMPATIBLE_ICON = TunableResourceKey(description='\n        Icon to be displayed when pie menu option is not compatible with\n        current posture of the sim.\n        ', default='PNG:missing_image', resource_types=sims4.resources.CompoundTypes.IMAGE)
Exemple #8
0
 def __init__(
         self,
         description='Represents a value that can be provided to the UI.',
         **kwargs):
     super().__init__(
         raw_int=Tunable(
             description=
             '\n                Provide an integer value.\n                ',
             tunable_type=int,
             default=0),
         raw_float=Tunable(
             description=
             '\n                Provide a floating-point value.\n                ',
             tunable_type=float,
             default=0),
         raw_string=Tunable(
             description=
             '\n                Provide a non-localized string.\n                ',
             tunable_type=str,
             default=''),
         raw_bool=Tunable(
             description=
             '\n                Provide a boolean value.\n                ',
             tunable_type=bool,
             default=False),
         resource_key=TunableResourceKey(
             description=
             '\n                Provide a resource key.\n                This is provided to the UI in the same format as\n                the ResourceKey AS3 class.\n                '
         ),
         locked_args={'null': None},
         default='null',
         description=description,
         **kwargs)
 def __init__(self, **kwargs):
     super().__init__(
         gender=TunableEnumEntry(
             description=
             "\n                The Sim's gender.\n                ",
             tunable_type=sim_info_types.Gender,
             needs_tuning=True,
             default=None),
         age_variant=TunableVariant(
             description=
             "\n                The sim's age for creation. Can be a literal age or random\n                between two ages.\n                ",
             literal=LiteralAge.TunableFactory(),
             random=RandomAge.TunableFactory(),
             needs_tuning=True),
         resource_key=OptionalTunable(
             description=
             '\n                If enabled, the Sim will be created using a saved SimInfo file.\n                ',
             tunable=TunableResourceKey(
                 description=
                 '\n                    The SimInfo file to use.\n                    ',
                 default=None,
                 resource_types=(sims4.resources.Types.SIMINFO, ))),
         full_name=OptionalTunable(
             description=
             "\n                If specified, then the Sim's name will be determined by this\n                localized string. Their first, last and full name will all be\n                set to this.\n                ",
             tunable=TunableLocalizedString()),
         tunable_tag_set=TunableReference(
             description=
             '\n                The set of tags that this template uses for CAS creation.\n                ',
             manager=services.get_instance_manager(
                 sims4.resources.Types.TAG_SET)),
         **kwargs)
class RelationshipBitCollection(metaclass=HashedTunedInstanceMetaclass,
                                manager=services.relationship_bit_manager()):
    __qualname__ = 'RelationshipBitCollection'
    INSTANCE_TUNABLES = {
        'name':
        TunableLocalizedString(
            export_modes=ExportModes.All,
            description='Name to be displayed for the collection.'),
        'icon':
        TunableResourceKey(
            'PNG:missing_image',
            resource_types=CompoundTypes.IMAGE,
            export_modes=ExportModes.All,
            description='Icon to be displayed for the collection.'),
        'collection_id':
        TunableEnumEntry(RelationshipBitCollectionUid,
                         RelationshipBitCollectionUid.Invalid,
                         export_modes=ExportModes.All,
                         description='The unique id of the relationship bit')
    }
    is_rel_bit = False

    @classmethod
    def _verify_tuning_callback(cls):
        validate_locked_enum_id(RelationshipBitCollection, cls.collection_id,
                                cls, RelationshipBitCollectionUid.Invalid)

    @classmethod
    def matches_bit(cls, bit_type):
        return cls.collection_id in bit_type.collection_ids
Exemple #11
0
class PaintingTexture(metaclass=TunedInstanceMetaclass,
                      manager=services.get_instance_manager(
                          sims4.resources.Types.RECIPE)):
    INSTANCE_TUNABLES = {
        'texture':
        TunableResourceKey(None,
                           resource_types=[sims4.resources.Types.TGA],
                           allow_none=True),
        'tests':
        TunableTestSet(),
        'canvas_types':
        TunableEnumFlags(
            CanvasType,
            CanvasType.NONE,
            description=
            '\n            The canvas types (generally, aspect ratios) with which this texture\n            may be used.\n            '
        )
    }

    @classmethod
    def _tuning_loaded_callback(cls):
        if cls.texture:
            cls._base_painting_state = PaintingState.from_key(cls.texture)
        else:
            cls._base_painting_state = None

    @classmethod
    def apply_to_object(cls, obj):
        obj.canvas_component.painting_state = cls._base_painting_state
class RandomDisplayName(HasTunableSingletonFactory, AutoFactoryInit):
    __qualname__ = 'RandomDisplayName'
    FACTORY_TUNABLES = {
        'overrides':
        TunableList(
            description=
            '\n            A list of random strings and icons to select randomly.\n            ',
            tunable=TunableTuple(
                new_display_name=TunableLocalizedStringFactory(),
                new_pie_menu_icon=TunableResourceKey(
                    default=None,
                    resource_types=sims4.resources.CompoundTypes.IMAGE),
                new_display_tooltip=OptionalTunable(
                    description=
                    '\n                    Tooltip to show on this pie menu option.\n                    ',
                    tunable=sims4.localization.TunableLocalizedStringFactory(
                    )),
                locked_args={'new_pie_menu_category': None})),
        'timeout':
        TunableSimMinute(
            description=
            '\n            The time it will take for a new string to be generated given the\n            same set of data.\n            ',
            minimum=0,
            default=10)
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._key_map = {}

    def get_display_names_gen(self):
        for override in self.overrides:
            yield override.new_display_name

    def get_display_name_and_result(self,
                                    interaction,
                                    target=DEFAULT,
                                    context=DEFAULT):
        context = interaction.context if context is DEFAULT else context
        target = interaction.target if target is DEFAULT else target
        key = (context.sim.id, 0 if target is None else target.id,
               interaction.affordance)
        random_names = getattr(context, 'random_names', dict())
        result = random_names.get(key)
        if result is not None:
            return (result, TestResult.NONE)
        now = services.time_service().sim_now
        result_and_timestamp = self._key_map.get(key)
        if result_and_timestamp is not None:
            time_delta = now - result_and_timestamp[1]
            if self.timeout > time_delta.in_minutes():
                self._key_map[key] = (result_and_timestamp[0], now)
                return (result_and_timestamp[0], TestResult.NONE)
        result = random.choice(self.overrides)
        random_names[key] = result
        setattr(context, 'random_names', random_names)
        self._key_map[key] = (result, now)
        return (result, TestResult.NONE)
Exemple #13
0
class CASStoriesTuning:
    CAS_STORIES = TunableTuple(
        description=
        '\n            Tuning for the CAS Stories sim-creation flow.\n            ',
        questions_per_pack=TunableMapping(
            description=
            '\n                Mapping between the number of packs available and the number of\n                base game questions and questions per pack that should be asked.\n                ',
            key_type=Tunable(
                description=
                '\n                    The number of available packs.\n                    ',
                tunable_type=int,
                default=0),
            value_type=TunableTuple(
                description=
                '\n                    The number of questions that should be asked.\n                    ',
                base_game_questions=Tunable(
                    description=
                    '\n                        The number of base game questions to ask.\n                        ',
                    tunable_type=int,
                    default=0),
                per_pack_questions=Tunable(
                    description=
                    '\n                        The number of pack questions to ask per pack.\n                        ',
                    tunable_type=int,
                    default=0),
                export_class_name='CasStoriesQuestionCountTuple'),
            tuple_name='CasStoriesQuestionsPerPackKeyValue'),
        funds_traits=TunableMapping(
            description=
            '\n                Mapping between specific traits and the amount of starting\n                household funds to give to a sim with that trait.\n                ',
            key_type=TunableReference(
                description=
                '\n                    Reference to the trait.\n                    ',
                manager=services.get_instance_manager(Types.TRAIT)),
            value_type=Tunable(
                description=
                '\n                    The household starting funds.\n                    ',
                tunable_type=int,
                default=0),
            tuple_name='CasStoriesFundsPerTraitKeyValue'),
        additional_sims_funds_percentage=TunableRange(
            description=
            '\n                If additional CAS Stories sims are added to a household, this is\n                the percentage of their normal funding to add to the household\n                funds as additonal funding. For instance, if we have 1 sim with\n                a 10,000 simoleon trait and a second sim with a 5,000 simoleon\n                trait and this is tuned to .5, the resulting household income\n                would be 10,000 + .5 * 5,000 = 12,500.\n                ',
            tunable_type=float,
            default=0.75,
            minimum=0,
            maximum=1),
        silouetted_sim_info=TunableResourceKey(
            description=
            '\n                The SimInfo file to use for the silouetted sim in CAS Stories.\n                ',
            default=None,
            resource_types=(sims4.resources.Types.SIMINFO, )),
        export_modes=ExportModes.ClientBinary,
        export_class_name='CasStoriesTuning')
Exemple #14
0
    class _MannequinTemplateResource(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {'resource_key': TunableResourceKey(description='\n                The SimInfo file to use.\n                ', default=None, resource_types=(sims4.resources.Types.SIMINFO,)), 'outfit': OptionalTunable(description='\n                If enabled, the mannequin will default to the specified outfit.\n                ', tunable=TunableTuple(description='\n                    The outfit to switch the mannequin into.\n                    ', outfit_category=TunableEnumEntry(description='\n                        The outfit category.\n                        ', tunable_type=OutfitCategory, default=OutfitCategory.EVERYDAY), outfit_index=TunableRange(description='\n                        The outfit index.\n                        ', tunable_type=int, minimum=0, maximum=4, default=0)))}

        def create_sim_info_data(self, sim_id):
            sim_info_data = SimInfoBaseWrapper(sim_id=sim_id)
            sim_info_data.load_from_resource(self.resource_key)
            if self.outfit is not None:
                outfit_to_set = (self.outfit.outfit_category, self.outfit.outfit_index)
                if sim_info_data.has_outfit(outfit_to_set):
                    sim_info_data.set_current_outfit(outfit_to_set)
                    sim_info_data.set_previous_outfit(outfit_to_set, force=True)
            return sim_info_data
Exemple #15
0
class ApplyCanvasOverlay(BaseTargetedLootOperation):
    FACTORY_TUNABLES = {'overlay_image': TunableResourceKey(description="\n            An image which will be composited with the texture of the target\n            object's canvas component. The resulting composited image will then\n            be set to be the object's new texture.\n            ", resource_types=[sims4.resources.Types.TGA], default=None)}

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

    def _apply_to_subject_and_target(self, subject, target, resolver):
        canvas_texture_id = target.canvas_component.get_canvas_texture_id()
        composite_target_effect = target.canvas_component.painting_state.effect
        op = CompositeImages(canvas_texture_id, composite_target_effect, target.id)
        with ProtocolBufferRollback(op.op.additional_composite_operations) as additional_composite_operations:
            additional_composite_operations.texture_hash = self.overlay_image.instance
        Distributor.instance().add_op_with_no_owner(op)
class UiPurchasePicker(UiDialogObjectPicker):
    __qualname__ = 'UiPurchasePicker'
    FACTORY_TUNABLES = {
        'categories':
        TunableList(
            description=
            '\n            A list of categories that will be displayed in the picker.\n            ',
            tunable=TunableTuple(
                description=
                '\n                Tuning for a single category in the picker.\n                ',
                tag=TunableEnumEntry(
                    description=
                    '\n                    A single tag used for filtering items.  If an item\n                    in the picker has this tag then it will be displayed\n                    in this category.\n                    ',
                    tunable_type=tag.Tag,
                    default=tag.Tag.INVALID),
                icon=TunableResourceKey(
                    description=
                    '\n                    Icon that represents this category.\n                    ',
                    default=None,
                    resource_types=sims4.resources.CompoundTypes.IMAGE),
                tooltip=TunableLocalizedString(
                    description=
                    '\n                    A localized string for the tooltip of the category.\n                    '
                )))
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.picker_type = ObjectPickerType.PURCHASE
        self.object_id = 0
        self.show_description = 0
        self.mailman_purchase = False

    def _validate_row(self, row):
        return isinstance(row, PurchasePickerRow)

    def _build_customize_picker(self, picker_data):
        picker_data.shop_picker_data.object_id = self.object_id
        picker_data.shop_picker_data.show_description = self.show_description
        picker_data.shop_picker_data.mailman_purchase = self.mailman_purchase
        for category in self.categories:
            category_data = picker_data.shop_picker_data.categories.add()
            category_data.tag_type = category.tag
            build_icon_info_msg((category.icon, None), None,
                                category_data.icon_info)
            category_data.description = category.tooltip
        for row in self.picker_rows:
            row_data = picker_data.shop_picker_data.row_data.add()
            row.populate_protocol_buffer(row_data)
Exemple #17
0
class VocalTrack(HasTunableReference,
                 metaclass=HashedTunedInstanceMetaclass,
                 manager=services.get_instance_manager(
                     sims4.resources.Types.RECIPE)):
    INSTANCE_TUNABLES = {
        'vocal_clip':
        TunableResourceKey(
            description=
            '\n            The propx file of the vox to play.\n            ',
            default=None,
            resource_types=(sims4.resources.Types.PROPX, )),
        'tests':
        TunableTestSet(
            description=
            '\n            Tests to verify if this song is available for the Sim to play.\n            '
        )
    }
Exemple #18
0
class InventoryTypeTuning:
    __qualname__ = 'InventoryTypeTuning'
    INVENTORY_TYPE_DATA = TunableMapping(
        description=
        '\n        A mapping of Inventory Type to any static information required by the\n        client to display inventory data as well information about allowances\n        for each InventoryType.\n        ',
        key_type=InventoryType,
        value_type=TunableTuple(
            description=
            '\n            Any information required by the client to display inventory data.\n            ',
            display_text=TunableLocalizedString(
                description=
                '\n                The name associated with this inventory type.\n                '
            ),
            icon=TunableResourceKey(
                description=
                '\n                The icon associated with this inventory type.\n                ',
                default=None,
                resource_types=CompoundTypes.IMAGE),
            skip_carry_pose_allowed=Tunable(
                description=
                '\n                If checked, an object tuned to be put away in this inventory\n                type will be allowed to skip the carry pose.  If unchecked, it\n                will not be allowed to skip the carry pose.\n                ',
                tunable_type=bool,
                default=False),
            put_away_allowed=Tunable(
                description=
                '\n                If checked, objects can be manually "put away" in this\n                inventory type. If unchecked, objects cannot be manually "put\n                away" in this inventory type.\n                ',
                tunable_type=bool,
                default=True)))
    GAMEPLAY_MODIFIERS = TunableMapping(
        description=
        "\n        A mapping of Inventory Type to the gameplay effects they provide. If an\n        inventory does not affect contained objects, it is fine to leave that\n        inventory's type out of this mapping.\n        ",
        key_type=InventoryType,
        value_type=TunableTuple(
            description='\n            Gameplay modifiers.\n            ',
            decay_modifiers=CommodityDecayModifierMapping(
                description=
                '\n                Multiply the decay rate of specific commodities by a tunable\n                integer in order to speed up or slow down decay while the\n                object is contained within this inventory. This modifier will\n                be multiplied with other modifiers on the object, if it has\n                any.\n                '
            )))
 def __init__(self, **kwargs):
     super().__init__(
         cancelable=Tunable(
             description=
             '\n                If this tutorial tip can be canceled.\n                ',
             tunable_type=bool,
             default=True),
         text=TunableLocalizedStringFactory(
             description=
             "\n                The text for this tip.\n                Token {0} is the active sim. i.e. {0.SimFirstName}\n                Token {1.String} is a 'wildcard' string to be used for things\n                like aspiration names or buff names during the tutorial.\n                "
         ),
         action_text=TunableLocalizedStringFactory(
             description=
             "\n                The action the user must make for this tip to satisfy.\n                Token {0} is the active sim. i.e. {0.SimFirstName}\n                Token {1.String} is a 'wildcard' string to be used for things\n                like aspiration names or buff names during the tutorial.\n                "
         ),
         timeout=TunableRange(
             description=
             '\n                How long, in seconds, until this tutorial tip times out.\n                ',
             tunable_type=int,
             default=1,
             minimum=1),
         ui_element=TunableEnumEntry(
             description=
             '\n                The UI element associated with this tutorial tip.\n                ',
             tunable_type=TutorialTipUiElement,
             default=TutorialTipUiElement.UI_INVALID),
         is_modal=Tunable(
             description=
             '\n                Enable if this tip should be modal.\n                Disable, if not.\n                ',
             tunable_type=bool,
             default=False),
         icon=TunableResourceKey(
             description=
             '\n                The icon to be displayed in a modal tutorial tip.\n                If Is Modal is disabled, this field can be ignored.\n                ',
             resource_types=sims4.resources.CompoundTypes.IMAGE,
             default=None),
         **kwargs)
Exemple #20
0
    def __init__(self, description='A model state.', **kwargs):
        def validate_definition_swap(instance_class, tunable_name, source,
                                     value):
            pass

        super().__init__(
            model=TunableResourceKey(
                description=
                '\n                The model file resource key.\n                ',
                default=None,
                resource_types=(sims4.resources.Types.MODEL, )),
            model_from_definition=TunableTuple(
                definition=TunableReference(
                    description=
                    '\n                    The model definition.\n                    ',
                    manager=services.definition_manager()),
                also_swap_definition=Tunable(
                    description=
                    '\n                    If True, the model swap operation will also swap the\n                    definition for the object. If false it will only change the\n                    model. We should only set this to True for sculpture\n                    objects.\n                    ',
                    tunable_type=bool,
                    default=False)),
            description=description,
            callback=validate_definition_swap,
            **kwargs)
class RelationshipBitCollection(metaclass=HashedTunedInstanceMetaclass,
                                manager=services.relationship_bit_manager()):
    INSTANCE_TUNABLES = {
        'name':
        TunableLocalizedString(
            description=
            '\n            Name to be displayed for the collection.\n            ',
            export_modes=ExportModes.All),
        'icon':
        TunableResourceKey(
            description=
            '\n            Icon to be displayed for the collection.\n            ',
            allow_none=True,
            resource_types=CompoundTypes.IMAGE,
            export_modes=ExportModes.All),
        'collection_id':
        TunableEnumEntry(
            description=
            '\n            The unique id of the relationship bit\n            ',
            tunable_type=RelationshipBitCollectionUid,
            default=RelationshipBitCollectionUid.Invalid,
            export_modes=ExportModes.All)
    }

    @classproperty
    def is_collection(cls):
        return True

    @classmethod
    def _verify_tuning_callback(cls):
        validate_locked_enum_id(RelationshipBitCollection, cls.collection_id,
                                cls, RelationshipBitCollectionUid.Invalid)

    @classmethod
    def matches_bit(cls, bit_type):
        return cls.collection_id in bit_type.collection_ids
 def __init__(self, **kwargs):
     super().__init__(key=TunableResourceKey(
         None, resource_types=sims4.resources.CompoundTypes.IMAGE),
                      description='The icon image to be displayed.',
                      **kwargs)
Exemple #23
0
class VideoComponent(HasTunableFactory,
                     NativeComponent,
                     component_name=types.VIDEO_COMPONENT,
                     key=2982943478):
    FACTORY_TUNABLES = {
        'video_display_type':
        TunableEnumEntry(
            description=
            "\n            How videos should be played. This option should be kept in line\n            with what the model expects. If you're unsure what that is, please\n            consult the modeler. Setting this to the wrong value will result in\n            broken behavior such as videos not playing, broken shaders, etc.\n            \n            NORMAL: Videos appear as if being played from a screen on the\n            object, e.g. TVs, computer, tablets.\n            \n            LIGHT_OVERLAY: Videos appear as if they are being projected onto\n            the object by a video projector.\n            ",
            tunable_type=VideoDisplayType,
            default=VideoDisplayType.NORMAL),
        'mute_speed':
        TunableEnumEntry(
            description=
            '\n            Game Speed at or above the mute speed will have the audio muted\n            when that speed is selected. \n            Pause(0) is always muted.\n            ',
            tunable_type=ClockSpeedMode,
            default=ClockSpeedMode.PAUSED),
        'speed_audio_clip_replacement':
        OptionalTunable(
            description=
            '\n            If enabled, when the speed changes to the value tuned on MUTE SPEED\n            the audio of the video will be muted but additionally this audio\n            clip will be played.\n            ',
            tunable=TunableResourceKey(
                description=
                '\n                Audio clip name to play when mute speed crosses its threshold\n                ',
                default=None,
                resource_types=(sims4.resources.Types.PROPX, )),
            enabled_name='play_clip_on_speed_change',
            disabled_name='no_replacement'),
        'distortion_speed':
        TunableEnumEntry(
            description=
            '\n            Game Speed at or above the distortion speed will have a distortion\n            effect applied to the video when that speed is selected. \n            Pause(0) will never distort.\n            ',
            tunable_type=ClockSpeedMode,
            default=ClockSpeedMode.PAUSED)
    }

    def __init__(self,
                 *args,
                 video_display_type=VideoDisplayType.NORMAL,
                 mute_speed=ClockSpeedMode.PAUSED,
                 distortion_speed=ClockSpeedMode.PAUSED,
                 speed_audio_clip_replacement=None,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.video_display_type = video_display_type
        self.mute_speed = mute_speed
        self.distortion_speed = distortion_speed
        self.speed_audio_clip_replacement = speed_audio_clip_replacement
        self._awareness_request = None

    def __repr__(self):
        if self.owner.video_playlist is None:
            return 'No clips queued'
        else:
            return repr(self.owner.video_playlist)

    @property
    def video_playlist_looping(self):
        return self.owner.video_playlist

    @video_playlist_looping.setter
    def video_playlist_looping(self, value):
        if value is None:
            self.set_video_clips()
        else:
            self.set_video_clips([value], loop_last=True)

    @property
    def video_playlist(self):
        return self.owner.video_playlist

    @video_playlist.setter
    def video_playlist(self, value):
        if value is None:
            self.set_video_clips()
        else:
            self.set_video_clips(value.clip_list,
                                 loop_last=value.loop_last,
                                 append_clip=value.append_clip)

    def on_add(self):
        self._awareness_request = AwarenessSourceRequest(
            self.owner, awareness_sources={AwarenessChannel.AUDIO_VOLUME: 1})
        self._awareness_request.start()

    def on_remove(self):
        if self._awareness_request is not None:
            self._awareness_request.stop()
            self._awareness_request = None

    @componentmethod
    def set_video_clips(self,
                        clip_names=[],
                        loop_last=False,
                        append_clip=False):
        if self.owner.video_playlist:
            version_id = self._next_version(
                self.owner.video_playlist.version_id)
        else:
            version_id = 0
        self.owner.video_playlist = VideoPlaylist(
            version_id, clip_names, loop_last, self.video_display_type,
            self.mute_speed, self.distortion_speed,
            self.speed_audio_clip_replacement, append_clip)

    @componentmethod
    def add_video_clips(self, clip_names, loop_last=False):
        if not clip_names:
            return
        if self.owner.video_playlist is None:
            self.set_video_clips(clip_names, loop_last)
        else:
            self.owner.video_playlist.append_clips(clip_names, loop_last)
            self.owner._resend_video_playlist()

    @staticmethod
    def _next_version(version_id):
        if version_id >= 65535:
            return 0
        else:
            return version_id + 1
Exemple #24
0
class SituationGoal(SituationGoalDisplayMixin,
                    metaclass=HashedTunedInstanceMetaclass,
                    manager=services.get_instance_manager(
                        sims4.resources.Types.SITUATION_GOAL)):
    INSTANCE_SUBCLASSES_ONLY = True
    IS_TARGETED = False
    INSTANCE_TUNABLES = {
        '_pre_tests':
        TunableSituationGoalPreTestSet(
            description=
            '\n            A set of tests on the player sim and environment that all must\n            pass for the goal to be given to the player. e.g. Player Sim\n            has cooking skill level 7.\n            ',
            tuning_group=GroupNames.TESTS),
        '_post_tests':
        TunableSituationGoalPostTestSet(
            description=
            '\n            A set of tests that must all pass when the player satisfies the\n            goal_test for the goal to be consider completed. e.g. Player\n            has Drunk Buff when Kissing another sim at Night.\n            ',
            tuning_group=GroupNames.TESTS),
        '_cancel_on_travel':
        Tunable(
            description=
            '\n            If set, this situation goal will cancel (technically, complete\n            with score overridden to 0 so that situation score is not\n            progressed) if situation changes zone.\n            ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.TESTS),
        '_environment_pre_tests':
        TunableSituationGoalEnvironmentPreTestSet(
            description=
            '\n            A set of sim independent pre tests.\n            e.g. There are five desks.\n            ',
            tuning_group=GroupNames.TESTS),
        'role_tags':
        TunableSet(
            TunableEnumEntry(Tag, Tag.INVALID),
            description=
            '\n            This goal will only be given to Sims in SituationJobs or Role\n            States marked with one of these tags.\n            '
        ),
        '_cooldown':
        TunableSimMinute(
            description=
            '\n            The cooldown of this situation goal.  Goals that have been\n            completed will not be chosen again for the amount of time that\n            is tuned.\n            ',
            default=600,
            minimum=0),
        '_iterations':
        Tunable(
            description=
            '\n             Number of times the player must perform the action to complete the goal\n             ',
            tunable_type=int,
            default=1),
        '_score':
        Tunable(
            description=
            '\n            The number of points received for completing the goal.\n            ',
            tunable_type=int,
            default=10),
        'score_on_iteration_complete':
        OptionalTunable(
            description=
            '\n            If enabled then we will add an amount of score to the situation\n            with every iteration of the situation goal completing.\n            ',
            tunable=Tunable(
                description=
                '\n                An amount of score that should be applied when an iteration\n                completes.\n                ',
                tunable_type=int,
                default=10)),
        '_pre_goal_loot_list':
        TunableList(
            description=
            '\n            A list of pre-defined loot actions that will applied to every\n            sim in the situation when this situation goal is started.\n             \n            Do not use this loot list in an attempt to undo changes made by\n            the RoleStates to the sim. For example, do not attempt\n            to remove buffs or commodities added by the RoleState.\n            ',
            tunable=SituationGoalLootActions.TunableReference()),
        '_goal_loot_list':
        TunableList(
            description=
            '\n            A list of pre-defined loot actions that will applied to every\n            sim in the situation when this situation goal is completed.\n             \n            Do not use this loot list in an attempt to undo changes made by\n            the RoleStates to the sim. For example, do not attempt\n            to remove buffs or commodities added by the RoleState.\n            ',
            tunable=SituationGoalLootActions.TunableReference()),
        'noncancelable':
        Tunable(
            description=
            '\n            Checking this box will prevent the player from canceling this goal in the whim system.',
            tunable_type=bool,
            default=False),
        'time_limit':
        Tunable(
            description=
            '\n            Timeout (in Sim minutes) for Sim to complete this goal. The default state of 0 means\n            time is unlimited. If the goal is not completed in time, any tuned penalty loot is applied.',
            tunable_type=int,
            default=0),
        'penalty_loot_list':
        TunableList(
            description=
            '\n            A list of pre-defined loot actions that will applied to the Sim who fails\n            to complete this goal within the tuned time limit.\n            ',
            tunable=SituationGoalLootActions.TunableReference()),
        'goal_awarded_notification':
        OptionalTunable(
            description=
            '\n            If enabled, this goal will have a notification associated with it.\n            It is up to whatever system awards the goal (e.g. the Whim system)\n            to display the notification when necessary.\n            ',
            tunable=TunableUiDialogNotificationSnippet()),
        'goal_completion_notification':
        OptionalTunable(tunable=UiDialogNotification.TunableFactory(
            description=
            '\n                A TNS that will fire when this situation goal is completed.\n                '
        )),
        'goal_completion_notification_and_modal_target':
        OptionalTunable(
            description=
            '\n            If enabled then we will use the tuned situation job to pick a\n            random sim in the owning situation with that job to be the target\n            sim of the notification and modal dialog.\n            ',
            tunable=TunableReference(
                description=
                '\n                The situation job that will be used to find a sim in the owning\n                situation to be the target sim.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.SITUATION_JOB))),
        'audio_sting_on_complete':
        TunableResourceKey(
            description=
            '\n            The sound to play when this goal is completed.\n            ',
            resource_types=(sims4.resources.Types.PROPX, ),
            default=None,
            allow_none=True,
            tuning_group=GroupNames.AUDIO),
        'goal_completion_modal_dialog':
        OptionalTunable(tunable=UiDialogOk.TunableFactory(
            description=
            '\n                A modal dialog that will fire when this situation goal is\n                completed.\n                '
        )),
        'visible_minor_goal':
        Tunable(
            description=
            '\n            Whether or not this goal should be displayed in the minor goals\n            list if this goal is for a player facing situation.\n            ',
            tunable_type=bool,
            default=True,
            tuning_group=GroupNames.UI),
        'display_type':
        TunableEnumEntry(
            description=
            '\n            How this goal is presented in user-facing situations.\n            ',
            tunable_type=SituationGoalDisplayType,
            default=SituationGoalDisplayType.NORMAL,
            tuning_group=GroupNames.UI)
    }

    @classmethod
    def can_be_given_as_goal(cls, actor, situation, **kwargs):
        if actor is not None:
            resolver = event_testing.resolver.DataResolver(
                actor.sim_info, None)
            result = cls._pre_tests.run_tests(resolver)
            if not result:
                return result
        else:
            resolver = GlobalResolver()
        environment_test_result = cls._environment_pre_tests.run_tests(
            resolver)
        if not environment_test_result:
            return environment_test_result
        return TestResult.TRUE

    def __init__(self,
                 sim_info=None,
                 situation=None,
                 goal_id=0,
                 count=0,
                 locked=False,
                 completed_time=None,
                 secondary_sim_info=None,
                 **kwargs):
        self._sim_info = sim_info
        self._secondary_sim_info = secondary_sim_info
        self._situation = situation
        self.id = goal_id
        self._on_goal_completed_callbacks = CallableList()
        self._completed_time = completed_time
        self._count = count
        self._locked = locked
        self._score_override = None
        self._goal_status_override = None
        self._setup = False

    def setup(self):
        self._setup = True

    def destroy(self):
        self.decommision()
        self._sim_info = None
        self._situation = None

    def decommision(self):
        if self._setup:
            self._decommision()

    def _decommision(self):
        self._on_goal_completed_callbacks.clear()

    def create_seedling(self):
        actor_id = 0 if self._sim_info is None else self._sim_info.sim_id
        target_sim_info = self.get_required_target_sim_info()
        target_id = 0 if target_sim_info is None else target_sim_info.sim_id
        secondary_target_id = 0 if self._secondary_sim_info is None else self._secondary_sim_info.sim_id
        seedling = situations.situation_serialization.GoalSeedling(
            type(self), actor_id, target_id, secondary_target_id, self._count,
            self._locked, self._completed_time)
        return seedling

    def register_for_on_goal_completed_callback(self, listener):
        self._on_goal_completed_callbacks.append(listener)

    def unregister_for_on_goal_completed_callback(self, listener):
        self._on_goal_completed_callbacks.remove(listener)

    def get_gsi_name(self):
        if self._iterations <= 1:
            return self.__class__.__name__
        return '{} {}/{}'.format(self.__class__.__name__, self._count,
                                 self._iterations)

    def on_goal_offered(self):
        if self._situation is None:
            return
        for sim in self._situation.all_sims_in_situation_gen():
            resolver = sim.get_resolver()
            for loots in self._pre_goal_loot_list:
                for loot in loots.goal_loot_actions:
                    loot.apply_to_resolver(resolver)

    def _display_goal_completed_dialogs(self):
        actor_sim_info = services.active_sim_info()
        target_sim_info = None
        if self.goal_completion_notification_and_modal_target is not None:
            possible_sims = list(
                self._situation.all_sims_in_job_gen(
                    self.goal_completion_notification_and_modal_target))
            if possible_sims:
                target_sim_info = random.choice(possible_sims)
            if target_sim_info is None:
                return
        resolver = DoubleSimResolver(actor_sim_info, target_sim_info)
        if self.goal_completion_notification is not None:
            notification = self.goal_completion_notification(actor_sim_info,
                                                             resolver=resolver)
            notification.show_dialog()
        if self.goal_completion_modal_dialog is not None:
            dialog = self.goal_completion_modal_dialog(actor_sim_info,
                                                       resolver=resolver)
            dialog.show_dialog()

    def _on_goal_completed(self, start_cooldown=True):
        if start_cooldown:
            self._completed_time = services.time_service().sim_now
        loot_sims = (self._sim_info, ) if self._situation is None else tuple(
            self._situation.all_sims_in_situation_gen())
        for loots in self._goal_loot_list:
            for loot in loots.goal_loot_actions:
                for sim in loot_sims:
                    loot.apply_to_resolver(sim.get_resolver())
        self._display_goal_completed_dialogs()
        with situations.situation_manager.DelayedSituationDestruction():
            self._on_goal_completed_callbacks(self, True)

    def _on_iteration_completed(self):
        self._on_goal_completed_callbacks(self, False)

    def force_complete(self,
                       target_sim=None,
                       score_override=None,
                       start_cooldown=True):
        self._score_override = score_override
        self._count = self._iterations
        self._on_goal_completed(start_cooldown=start_cooldown)

    def _valid_event_sim_of_interest(self, sim_info):
        return self._sim_info is None or self._sim_info is sim_info

    def handle_event(self, sim_info, event, resolver):
        if not self._valid_event_sim_of_interest(sim_info):
            return
        if self._run_goal_completion_tests(sim_info, event, resolver):
            self._count += 1
            if self._count >= self._iterations:
                self._on_goal_completed()
            else:
                self._on_iteration_completed()

    def _run_goal_completion_tests(self, sim_info, event, resolver):
        return self._post_tests.run_tests(resolver)

    def should_autocomplete_on_load(self, previous_zone_id):
        if self._cancel_on_travel:
            zone_id = services.current_zone_id()
            if previous_zone_id != zone_id:
                return True
        return False

    def get_actual_target_sim_info(self):
        pass

    @property
    def sim_info(self):
        return self._sim_info

    def get_required_target_sim_info(self):
        pass

    def get_secondary_sim_info(self):
        return self._secondary_sim_info

    @property
    def created_time(self):
        pass

    @property
    def completed_time(self):
        return self._completed_time

    def is_on_cooldown(self):
        if self._completed_time is None:
            return False
        time_since_last_completion = services.time_service(
        ).sim_now - self._completed_time
        return time_since_last_completion < interval_in_sim_minutes(
            self._cooldown)

    def get_localization_tokens(self):
        target_sim_info = self.get_required_target_sim_info()
        return (self._numerical_token, self._sim_info, target_sim_info,
                self._secondary_sim_info)

    def get_display_name(self):
        display_name = self.display_name
        if display_name is not None:
            return display_name(*self.get_localization_tokens())

    def get_display_tooltip(self):
        display_tooltip = self.display_tooltip
        if display_tooltip is not None:
            return display_tooltip(*self.get_localization_tokens())

    @property
    def score(self):
        if self._score_override is not None:
            return self._score_override
        return self._score

    @property
    def goal_status_override(self):
        return self._goal_status_override

    @property
    def completed_iterations(self):
        return self._count

    @property
    def max_iterations(self):
        return self._iterations

    @property
    def _numerical_token(self):
        return self.max_iterations

    @property
    def locked(self):
        return self._locked

    def toggle_locked_status(self):
        self._locked = not self._locked

    def validate_completion(self):
        if self._completed_time is not None:
            return
        if self.completed_iterations < self.max_iterations:
            return
        self.force_complete()

    def show_goal_awarded_notification(self):
        if self.goal_awarded_notification is None:
            return
        icon_override = IconInfoData(icon_resource=self.display_icon)
        secondary_icon_override = IconInfoData(obj_instance=self._sim_info)
        notification = self.goal_awarded_notification(self._sim_info)
        notification.show_dialog(
            additional_tokens=self.get_localization_tokens(),
            icon_override=icon_override,
            secondary_icon_override=secondary_icon_override)
Exemple #25
0
class MusicTrack(metaclass=HashedTunedInstanceMetaclass,
                 manager=services.get_instance_manager(
                     sims4.resources.Types.RECIPE)):
    INSTANCE_TUNABLES = {
        'music_clip':
        OptionalTunable(
            description=
            '\n            If enabled, the music clip for music interactions. If disabled,\n            make sure you have vocals tuned.\n            ',
            tunable=TunableResourceKey(
                description=
                '\n                The propx file of the music clip to play.\n                ',
                needs_tuning=False,
                resource_types=(sims4.resources.Types.PROPX, ))),
        'length':
        TunableRealSecond(
            description=
            "\n            The length of the clip in real seconds.  This should be a part of\n            the propx's file name.\n            ",
            needs_tuning=False,
            default=30,
            minimum=0),
        'buffer':
        TunableRealSecond(
            description=
            "\n            A buffer added to the track length.  This is used to prevent the\n            audio from stopping before it's finished.\n            ",
            needs_tuning=False,
            default=0),
        'check_for_unlock':
        Tunable(
            description=
            "\n            Whether or not to check the Sim's Unlock Component to determine if\n            they can play the song.  Currently, only clips that are meant to be\n            unlocked by the Write Song interaction should have this set to true.\n            ",
            needs_tuning=False,
            tunable_type=bool,
            default=False),
        'music_track_name':
        OptionalTunable(
            description=
            "\n            If the clip is of a song, this is its name. The name is shown in the\n            Pie Menu when picking specific songs to play.\n            \n            If the clip isn't a song, like clips used for the Practice or Write\n            Song interactions, this does not need to be tuned.\n            ",
            tunable=TunableLocalizedStringFactory(
                description=
                "\n                The track's name.\n                "),
            enabled_by_default=True),
        'tests':
        TunableTestSet(
            description=
            '\n            Tests to verify if this song is available for the Sim to play.\n            '
        ),
        'moods':
        TunableList(
            description=
            "\n            A list of moods that will be used to determine which song a Sim will\n            play autonomously.  If a Sim doesn't know any songs that their\n            current mood, they'll play anything.\n            ",
            tunable=TunableReference(manager=services.mood_manager()),
            needs_tuning=True),
        'vocals':
        TunableMapping(
            description=
            "\n            A mapping of participants and their potential vocal tracks. Each\n            participant that has a vocal track that tests successfully will\n            sing when the music starts.\n            \n            Note: The interaction's resolver will be passed into the vocal\n            track tests, so use the same participant in those tests.\n            ",
            key_name='participant',
            value_name='vocal_tracks',
            key_type=TunableEnumEntry(
                description=
                '\n                The participant who should sing vocals when the music starts.\n                ',
                tunable_type=ParticipantTypeSim,
                default=ParticipantTypeSim.Actor),
            value_type=TunableList(
                description=
                '\n                If this music track has vocals, add them here.  The first track that\n                passes its test will be used.  If no tracks pass their test, none\n                will be used.\n                ',
                tunable=VocalTrack.TunableReference()))
    }

    @classmethod
    def _verify_tuning_callback(cls):
        if cls.music_clip is None and not cls.vocals:
            logger.error('{} does not have music or vocals tuned.',
                         cls,
                         owner='rmccord')

    @classproperty
    def tuning_tags(cls):
        return EMPTY_SET
class AwayAction(HasTunableReference, metaclass=HashedTunedInstanceMetaclass, manager=services.get_instance_manager(sims4.resources.Types.AWAY_ACTION)):
    __qualname__ = 'AwayAction'
    INSTANCE_TUNABLES = {'_exit_conditions': TunableList(description='\n                A list of exit conditions for this away action. When exit\n                conditions are met then the away action ends and the default\n                away action is reapplied.\n                ', tunable=TunableTuple(conditions=TunableList(description='\n                        A list of conditions that all must be satisfied for the\n                        group to be considered satisfied.\n                        ', tunable=TunableAwayActionCondition(description='\n                            A condition for an away action.\n                            ')))), '_periodic_stat_changes': PeriodicStatisticChange.TunableFactory(description='\n                Periodic stat changes that this away action applies while it\n                is active.\n                '), 'icon': TunableResourceKey(description='\n                Icon that represents the away action in on the sim skewer.\n                ', default=None, resource_types=sims4.resources.CompoundTypes.IMAGE, tuning_group=GroupNames.UI), 'tooltip': TunableLocalizedStringFactory(description='\n                The tooltip shown on the icon that represents the away action.\n                ', tuning_group=GroupNames.UI), 'pie_menu_tooltip': TunableLocalizedStringFactory(description='\n                The tooltip shown in the pie menu for this away action.\n                ', tuning_group=GroupNames.UI), '_tests': TunableTestSet(description='\n                Tests that determine if this away action is applicable.  These\n                tests do not ensure that the conditions are still met\n                throughout the duration that the away action is applied.\n                '), '_display_name': TunableLocalizedStringFactory(description='\n                The name given to the away action when the user sees it in the\n                pie menu.\n                ', tuning_group=GroupNames.UI), '_display_name_text_tokens': LocalizationTokens.TunableFactory(description="\n                Localization tokens to be passed into 'display_name'.\n                For example, you could use a participant or you could also pass\n                in statistic and commodity values\n                ", tuning_group=GroupNames.UI), '_available_when_instanced': Tunable(description="\n                If this away action is able to be applied when the sim is still\n                instanced.  If the sim becomes instanced while the away action\n                is running we will not stop running it.\n                \n                This should only be true in special cases such as with careers.\n                \n                PLEASE ASK A GPE ABOUT MAKING THIS TRUE BEFORE DOING SO.  YOU\n                PROBABLY DON'T WANT THIS.\n                ", tunable_type=bool, default=False), '_preroll_commodities': TunableList(description='\n                A list of commodities that will be used to run preroll\n                if the sim loaded with this away action.\n                ', tunable=TunableReference(description='\n                    The commodity that is used to solve for preroll if the\n                    sim had this away action on them when they are being loaded.\n                    \n                    This is used to help preserve the fiction of what that sim was\n                    doing when the player returns to the lot.  EX: make the sim\n                    garden if they were using the gardening away action. \n                    ', manager=services.get_instance_manager(sims4.resources.Types.STATISTIC))), '_preroll_static_commodities': TunableList(description='\n                A list of static commodities that will be used to run preroll\n                if the sim loaded with this away action.\n                ', tunable=StaticCommodity.TunableReference(description='\n                    The static commodity that is used to solve for preroll if the\n                    sim had this away action on them when they are being loaded.\n                    \n                    This is used to help preserve the fiction of what that sim was\n                    doing when the player returns to the lot.  EX: make the sim\n                    garden if they were using the gardening away action. \n                    ')), '_apply_on_load_tags': TunableSet(description='\n                A set of tags that are are compared to interaction tags that\n                the sim was running when they became uninstantiated.  If there\n                are any matching tags then this away action will be applied\n                automatically to that sim rather than the default away action.\n                ', tunable=TunableEnumEntry(description='\n                    A single tag that will be compared to the interaction tags.\n                    ', tunable_type=tag.Tag, default=tag.Tag.INVALID)), '_disabled_when_running': OptionalTunable(description='\n                The availability of this away action when it is already the\n                active away action on the sim.\n                ', tunable=TunableLocalizedStringFactory(description='\n                    The text that displays in the tooltip string when this\n                    away action is not available because it is already the\n                    active away action.\n                    '), disabled_name='available_when_running', enabled_name='disabled_when_running'), 'mood_list': TunableList(description='\n                A list of possible moods this AwayAction may associate with.\n                ', tunable=TunableReference(description='\n                    A mood associated with this AwayAction.\n                    ', manager=services.mood_manager()))}

    def __init__(self, tracker, target=None):
        self._tracker = tracker
        self._target = target
        self._conditional_actions_manager = ConditionalActionManager()
        self._periodic_stat_changes_instance = self._periodic_stat_changes(self)
        self._state = AwayActionState.INITIALIZED

    @classmethod
    def should_run_on_load(cls, sim_info):
        for interaction_data in sim_info.si_state.interactions:
            interaction = services.get_instance_manager(sims4.resources.Types.INTERACTION).get(interaction_data.interaction)
            if interaction is None:
                pass
            while len(interaction.get_category_tags() & cls._apply_on_load_tags) > 0:
                return True
        return False

    @classmethod
    def get_commodity_preroll_list(cls):
        if cls._preroll_commodities:
            return cls._preroll_commodities

    @classmethod
    def get_static_commodity_preroll_list(cls):
        if cls._preroll_static_commodities:
            return cls._preroll_static_commodities

    @property
    def sim_info(self):
        return self._tracker.sim_info

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

    @property
    def target(self):
        return self._target

    @classproperty
    def available_when_instanced(cls):
        return cls._available_when_instanced

    @property
    def is_running(self):
        return self._state == AwayActionState.RUNNING

    def run(self, callback):
        if self._state == AwayActionState.RUNNING:
            logger.callstack('Attempting to start away action that is already running.', owner='jjacobson')
            return
        self._periodic_stat_changes_instance.run()
        if self._exit_conditions:
            self._conditional_actions_manager.attach_conditions(self, self._exit_conditions, callback)
        self._state = AwayActionState.RUNNING

    def stop(self):
        if self._state == AwayActionState.STOPPED:
            logger.callstack('Attempting to stop away action that is already stopped.', owner='jjacobson')
            return
        self._periodic_stat_changes_instance.stop()
        if self._exit_conditions:
            self._conditional_actions_manager.detach_conditions(self)
        self._state = AwayActionState.STOPPED

    @flexmethod
    def get_participant(cls, inst, participant_type=ParticipantType.Actor, **kwargs):
        inst_or_cl = inst if inst is not None else cls
        participants = inst_or_cl.get_participants(participant_type=participant_type, **kwargs)
        if not participants:
            return
        if len(participants) > 1:
            raise ValueError('Too many participants returned for {}!'.format(participant_type))
        return next(iter(participants))

    @flexmethod
    def get_participants(cls, inst, participant_type, sim_info=DEFAULT, target=DEFAULT) -> set:
        inst_or_cls = inst if inst is not None else cls
        sim_info = inst.sim_info if sim_info is DEFAULT else sim_info
        target = inst.target if target is DEFAULT else target
        if sim_info is None:
            logger.error('Sim info is None when trying to get participants for Away Action {}.', inst_or_cls, owner='jjacobson')
            return ()
        results = set()
        participant_type = int(participant_type)
        if participant_type & ParticipantType.Actor:
            results.add(sim_info)
        if participant_type & ParticipantType.Lot:
            zone = services.get_zone(sim_info.zone_id, allow_uninstantiated_zones=True)
            results.add(zone.lot)
        if participant_type & ParticipantType.TargetSim and target is not None:
            results.add(target)
        return tuple(results)

    @flexmethod
    def get_resolver(cls, inst, **away_action_parameters):
        inst_or_cls = inst if inst is not None else cls
        return event_testing.resolver.AwayActionResolver(inst_or_cls, **away_action_parameters)

    @flexmethod
    def get_localization_tokens(cls, inst, **away_action_parameters):
        inst_or_cls = inst if inst is not None else cls
        tokens = inst_or_cls._display_name_text_tokens.get_tokens(inst_or_cls.get_resolver(**away_action_parameters))
        return tokens

    @flexmethod
    def test(cls, inst, sim_info=DEFAULT, **away_action_parameters):
        inst_or_cls = inst if inst is not None else cls
        sim_info = inst.sim_info if sim_info is DEFAULT else sim_info
        current_away_action = sim_info.current_away_action
        if inst_or_cls._disabled_when_running and current_away_action is not None and isinstance(current_away_action, cls):
            return TestResult(False, 'Cannot run away action when it is already running', tooltip=inst_or_cls._disabled_when_running)
        resolver = inst_or_cls.get_resolver(sim_info=sim_info, **away_action_parameters)
        if inst is None:
            condition_actions_manager = ConditionalActionManager()
        else:
            condition_actions_manager = inst._conditional_actions_manager
        if inst_or_cls._exit_conditions and condition_actions_manager.callback_will_trigger_immediately(resolver, inst_or_cls._exit_conditions):
            return TestResult(False, 'Away Action cannot run since exit conditions will satisfy immediately.')
        return inst_or_cls._tests.run_tests(resolver)

    @flexmethod
    def get_display_name(cls, inst, *tokens, **away_action_parameters):
        inst_or_cls = inst if inst is not None else cls
        localization_tokens = inst_or_cls.get_localization_tokens(**away_action_parameters)
        return inst_or_cls._display_name(*localization_tokens + tokens)
Exemple #27
0
 def __init__(self, *args, **kwargs):
     super().__init__(texture_key=TunableResourceKey(description='\n                The resource key that we want to switch the painting state to.\n                ', resource_types=[sims4.resources.Types.TGA], default=None), **kwargs)
Exemple #28
0
class VetClinicDiagnosisSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'vet_job':
        TunableSituationJobAndRoleState(
            description=
            '\n            The job and role which the vet Sim is placed into.\n            ',
            tuning_group=GroupNames.ROLES),
        'pet_job':
        TunableSituationJobAndRoleState(
            description=
            '\n            The job and role which the pet Sim is placed into.\n            ',
            tuning_group=GroupNames.ROLES),
        'undiscovered_sickness_text':
        TunableLocalizedStringFactory(
            description=
            '\n            Text to use if a sickness is undiscovered.\n            ',
            tuning_group=GroupNames.UI),
        'undiscovered_symptom_text':
        TunableLocalizedStringFactory(
            description=
            '\n            Text to use if a symptom is undiscovered.\n            ',
            tuning_group=GroupNames.UI),
        '_progress_meter_settings':
        StatBasedSituationMeterData.TunableFactory(
            description=
            '\n            The meter used to track the progress of the diagnosis.\n            ',
            tuning_group=GroupNames.SITUATION,
            locked_args={'_meter_id': DIAGNOSIS_PROGRESS_METER_ID}),
        '_stress_meter_settings':
        StatBasedSituationMeterData.TunableFactory(
            description=
            '\n            The meter used to track the stress level of the patient.\n            ',
            tuning_group=GroupNames.SITUATION,
            locked_args={'_meter_id': PATIENT_STRESS_METER_ID}),
        'audio_sting_on_symptom_discovery':
        TunableResourceKey(
            description=
            '\n            The sound to play when a symptom is discovered.\n            ',
            resource_types=(sims4.resources.Types.PROPX, ),
            default=None,
            allow_none=True,
            tuning_group=GroupNames.AUDIO)
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_buff', 'targeted_situation', '_resident_job',
        '_relationship_between_job_members', 'force_invite_only',
        'screen_slam_gold', 'screen_slam_silver', 'screen_slam_bronze',
        'screen_slam_no_medal', 'main_goal', '_main_goal_visibility_test',
        'minor_goal_chains', '_hidden_scoring_override',
        '_implies_greeted_status', '_level_data', 'weight_multipliers',
        'recommended_job_object_notification', 'recommended_job_object_text'
    ) + Situation.SITUATION_START_FROM_UI_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, WaitForDiagnosisActorsState),
                SituationStateData(2, DiagnosisSituationState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.vet_job.job, cls.vet_job.role_state),
                (cls.pet_job.job, cls.pet_job.role_state)]

    @classmethod
    def default_job(cls):
        return cls.vet_job.job

    @classmethod
    def get_tuned_jobs(cls):
        return [cls.vet_job.job, cls.pet_job.job]

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

    def __init__(self, *arg, **kwargs):
        super().__init__(*arg, **kwargs)
        self._started_from_load = services.current_zone(
        ) is None or not services.current_zone().is_zone_running

    @property
    def situation_display_type(self):
        return SituationDisplayType.VET

    @property
    def situation_display_priority(self):
        return SituationDisplayPriority.VET

    def get_pet(self):
        if self._started_from_load:
            return self._get_sim_from_guest_list(self.pet_job.job)
        return next(iter(self.all_sims_in_job_gen(self.pet_job.job)), None)

    def get_vet(self):
        if self._started_from_load:
            return self._get_sim_from_guest_list(self.vet_job.job)
        return next(iter(self.all_sims_in_job_gen(self.vet_job.job)), None)

    def start_situation(self):
        super().start_situation()
        self._register_test_event(TestEvent.BusinessClosed)
        self._change_state(WaitForDiagnosisActorsState())

    def handle_event(self, sim_info, event, resolver):
        if event == TestEvent.BusinessClosed:
            self._self_destruct()

    def get_create_op(self, *args, **kwargs):
        return distributor.ops.SituationStartOp(
            self, self.build_situation_start_message(), immediate=True)

    def on_added_to_distributor(self):
        super().on_added_to_distributor()
        if self._started_from_load and None not in (self.get_pet(),
                                                    self.get_vet()):
            self._change_state(DiagnosisSituationState())

    def build_situation_start_message(self):
        msg = super().build_situation_start_message()
        with ProtocolBufferRollback(msg.meter_data) as meter_data_msg:
            self._progress_meter_settings.build_data_message(meter_data_msg)
        with ProtocolBufferRollback(msg.meter_data) as meter_data_msg:
            self._stress_meter_settings.build_data_message(meter_data_msg)
        return msg

    def has_offered_goals(self):
        return False

    def refresh_situation_goals(self, resolver=None):
        self._send_goal_update_to_client(resolver=resolver)

    def send_icon_update_to_client(self):
        msg = Situations_pb2.SituationIconUpdate()
        msg.situation_id = self.id
        build_icon_info_msg(self.get_pet().get_icon_info_data(), None,
                            msg.icon_info)
        msg.icon_info.control_id = ICON_CONTROL_ID
        op = distributor.ops.SituationIconUpdateOp(msg)
        Distributor.instance().add_op(self, op)

    def _send_goal_update_to_client(self, resolver=None, completed_goal=None):
        pet = self.get_pet()
        if pet is None:
            return
        sickness = pet.sim_info.current_sickness
        op = distributor.ops.SituationGoalUpdateOp(
            self._create_situation_goal_update_msg(pet, sickness, resolver))
        Distributor.instance().add_op(self, op)

    def _create_situation_goal_update_msg(self, pet, sickness, resolver):
        msg = Situations_pb2.SituationGoalsUpdate()
        msg.goal_status = UiSituationGoalStatus.COMPLETED
        msg.situation_id = self.id
        self._set_major_goal_data(pet, sickness, msg.major_goal)
        self._set_minor_goals_data(pet, msg.goals)
        self._handle_completed_goal(resolver, sickness, msg)
        return msg

    def _handle_completed_goal(self, resolver, sickness, msg):
        recently_discovered_sickness = None if resolver is None else resolver.get_resolved_arg(
            'discovered_sickness')
        if recently_discovered_sickness is sickness:
            msg.completed_goal_id = SICKNESS_MAJOR_GOAL_ID
            return msg
        else:
            recently_discovered_symptom = None if resolver is None else resolver.get_resolved_arg(
                'discovered_symptom')
            if recently_discovered_symptom is not None:
                msg.completed_goal_id = SICKNESS_MINOR_GOAL_IDS[
                    sickness.get_ordered_symptoms().index(
                        recently_discovered_symptom)]
                return msg
        return msg

    def _set_major_goal_data(self, pet, sickness, major_goal_msg):
        is_sickness_discovered = False if sickness is None else pet.sim_info.sickness_tracker.has_discovered_sickness
        major_goal_name = sickness.display_name(
        ) if is_sickness_discovered else self.undiscovered_sickness_text()
        major_goal_msg.goal_id = SICKNESS_MAJOR_GOAL_ID
        major_goal_msg.goal_name = major_goal_name
        major_goal_msg.max_iterations = 1
        major_goal_msg.current_iterations = 1 if is_sickness_discovered else 0
        if self.main_goal_audio_sting is not None:
            major_goal_msg.audio_sting.type = self.main_goal_audio_sting.type
            major_goal_msg.audio_sting.group = self.main_goal_audio_sting.group
            major_goal_msg.audio_sting.instance = self.main_goal_audio_sting.instance

    def _set_minor_goals_data(self, pet, goals_msg):
        discovered_symptoms = pet.sim_info.sickness_tracker.discovered_symptoms
        while True:
            for i in range(0 if pet.sim_info.current_sickness is None else len(
                    pet.sim_info.current_sickness.symptoms)):
                is_symptom_discovered = len(
                    discovered_symptoms
                ) > i and pet.sim_info.was_symptom_discovered(
                    discovered_symptoms[i])
                symptom_goal_name = discovered_symptoms[i].display_name(
                ) if is_symptom_discovered else self.undiscovered_symptom_text(
                )
                with ProtocolBufferRollback(goals_msg) as goal_msg:
                    goal_msg.goal_id = SICKNESS_MINOR_GOAL_IDS[i]
                    goal_msg.goal_name = symptom_goal_name
                    goal_msg.max_iterations = 1
                    goal_msg.current_iterations = 1 if is_symptom_discovered else 0
                    if self.audio_sting_on_symptom_discovery is not None:
                        goal_msg.audio_sting.type = self.audio_sting_on_symptom_discovery.type
                        goal_msg.audio_sting.group = self.audio_sting_on_symptom_discovery.group
                        goal_msg.audio_sting.instance = self.audio_sting_on_symptom_discovery.instance
Exemple #29
0
class Skill(HasTunableReference,
            statistics.continuous_statistic_tuning.TunedContinuousStatistic,
            metaclass=HashedTunedInstanceMetaclass,
            manager=services.get_instance_manager(
                sims4.resources.Types.STATISTIC)):
    __qualname__ = 'Skill'
    SKILL_LEVEL_LIST = TunableMapping(
        key_type=TunableEnumEntry(SkillLevelType, SkillLevelType.MAJOR),
        value_type=TunableList(
            Tunable(int, 0),
            description=
            'The level boundaries for skill type, specified as a delta from the previous value'
        ),
        export_modes=ExportModes.All)
    SKILL_EFFECTIVENESS_GAIN = TunableMapping(
        key_type=TunableEnumEntry(SkillEffectiveness,
                                  SkillEffectiveness.STANDARD),
        value_type=TunableCurve(),
        description='Skill gain points based on skill effectiveness.')
    DYNAMIC_SKILL_INTERVAL = TunableRange(
        description=
        '\n        Interval used when dynamic loot is used in a\n        PeriodicStatisticChangeElement.\n        ',
        tunable_type=float,
        default=1,
        minimum=1)
    INSTANCE_TUNABLES = {
        'stat_name':
        TunableLocalizedString(
            description=
            '\n            Localized name of this Statistic\n            ',
            export_modes=ExportModes.All),
        'ad_data':
        TunableList(
            description=
            '\n            A list of Vector2 points that define the desire curve for this\n            commodity.\n            ',
            tunable=TunableVector2(
                description=
                '\n                Point on a Curve\n                ',
                default=sims4.math.Vector2(0, 0))),
        'weight':
        Tunable(
            description=
            "\n            The weight of the Skill with regards to autonomy.  It's ignored \n            for the purposes of sorting stats, but it's applied when scoring \n            the actual statistic operation for the SI.\n            ",
            tunable_type=float,
            default=0.5),
        'skill_level_type':
        TunableEnumEntry(
            description='\n            Skill level list to use.\n            ',
            tunable_type=SkillLevelType,
            default=SkillLevelType.MAJOR,
            export_modes=ExportModes.All),
        'locked_description':
        TunableLocalizedString(
            description=
            "\n            The skill description when it's locked.\n            ",
            export_modes=ExportModes.All),
        'skill_description':
        TunableLocalizedString(
            description=
            "\n            The skill's normal description.\n            ",
            export_modes=ExportModes.All),
        'is_default':
        Tunable(
            description=
            '\n            Whether Sim will default has this skill.\n            ',
            tunable_type=bool,
            default=False),
        'genders':
        TunableSet(
            description=
            '\n            Skill allowed gender, empty set means not specified\n            ',
            tunable=TunableEnumEntry(tunable_type=sim_info_types.Gender,
                                     default=None,
                                     export_modes=ExportModes.All)),
        'ages':
        TunableSet(
            description=
            '\n            Skill allowed ages, empty set means not specified\n            ',
            tunable=TunableEnumEntry(tunable_type=sim_info_types.Age,
                                     default=None,
                                     export_modes=ExportModes.All)),
        'entitlement':
        TunableEntitlement(
            description=
            '\n            Entitlement required to use this skill.\n            '
        ),
        'icon':
        TunableResourceKey(
            description=
            '\n            Icon to be displayed for the Skill.\n            ',
            default='PNG:missing_image',
            resource_types=sims4.resources.CompoundTypes.IMAGE,
            export_modes=ExportModes.All),
        'tags':
        TunableList(
            description=
            '\n            The associated categories of the skill\n            ',
            tunable=TunableEnumEntry(tunable_type=tag.Tag,
                                     default=tag.Tag.INVALID)),
        'priority':
        Tunable(
            description=
            '\n            Skill priority.  Higher priority skill will trump other skills when\n            being displayed on the UI side. When a sim gains multiple skills at\n            the same time only the highest priority one will display a progress\n            bar over its head.\n            ',
            tunable_type=int,
            default=1,
            export_modes=ExportModes.All),
        'statistic_multipliers':
        TunableMapping(
            description=
            '\n            Multipliers this skill applies to other statistics based on its\n            value.\n            ',
            key_type=TunableReference(
                description=
                '\n                The statistic this multiplier will be applied to.\n                ',
                manager=services.statistic_manager(),
                reload_dependent=True),
            value_type=TunableTuple(
                curve=TunableCurve(
                    description=
                    '\n                    Tunable curve where the X-axis defines the skill level, and\n                    the Y-axis defines the associated multiplier.\n                    ',
                    x_axis_name='Skill Level',
                    y_axis_name='Multiplier'),
                direction=TunableEnumEntry(
                    description=
                    "\n                    Direction where the multiplier should work on the\n                    statistic.  For example, a tuned decrease for an object's\n                    brokenness rate will not also increase the time it takes to\n                    repair it.\n                    ",
                    tunable_type=StatisticChangeDirection,
                    default=StatisticChangeDirection.INCREASE),
                use_effective_skill=Tunable(
                    description=
                    '\n                    If checked, this modifier will look at the current\n                    effective skill value.  If unchecked, this modifier will\n                    look at the actual skill value.\n                    ',
                    tunable_type=bool,
                    needs_tuning=True,
                    default=True)),
            tuning_group=GroupNames.MULTIPLIERS),
        'success_chance_multipliers':
        TunableList(
            description=
            '\n            Multipliers this skill applies to the success chance of\n            affordances.\n            ',
            tunable=TunableSkillMultiplier(),
            tuning_group=GroupNames.MULTIPLIERS),
        'monetary_payout_multipliers':
        TunableList(
            description=
            '\n            Multipliers this skill applies to the monetary payout amount of\n            affordances.\n            ',
            tunable=TunableSkillMultiplier(),
            tuning_group=GroupNames.MULTIPLIERS),
        'next_level_teaser':
        TunableList(
            description=
            '\n            Tooltip which describes what the next level entails.\n            ',
            tunable=TunableLocalizedString(),
            export_modes=(ExportModes.ClientBinary, )),
        'level_data':
        TunableMapping(
            description=
            '\n            Level-specific information, such as notifications to be displayed to\n            level up.\n            ',
            key_type=int,
            value_type=TunableTuple(
                level_up_notification=UiDialogNotification.TunableFactory(
                    description=
                    '\n                    The notification to display when the Sim obtains this level.\n                    The text will be provided two tokens: the Sim owning the\n                    skill and a number representing the 1-based skill level\n                    ',
                    locked_args={
                        'text_tokens':
                        DEFAULT,
                        'icon':
                        None,
                        'primary_icon_response':
                        UiDialogResponse(text=None,
                                         ui_request=UiDialogResponse.
                                         UiDialogUiRequest.SHOW_SKILL_PANEL),
                        'secondary_icon':
                        None
                    }),
                level_up_screen_slam=OptionalTunable(
                    description=
                    '\n                    Screen slam to show when reaches this skill level.\n                    Localization Tokens: Sim - {0.SimFirstName}, Skill Name - \n                    {1.String}, Skill Number - {2.Number}\n                    ',
                    tunable=ui.screen_slam.TunableScreenSlamSnippet(),
                    tuning_group=GroupNames.UI))),
        'mood_id':
        TunableReference(
            description=
            '\n            When this mood is set and active sim matches mood, the UI will \n            display a special effect on the skill bar to represent that this \n            skill is getting a bonus because of the mood.\n            ',
            manager=services.mood_manager(),
            export_modes=ExportModes.All),
        'stat_asm_param':
        TunableStatAsmParam.TunableFactory(),
        'tutorial':
        TunableReference(
            description=
            '\n            Tutorial instance for this skill. This will be used to bring up the \n            skill lesson from the first notification for Sim to know this skill.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.TUTORIAL),
            class_restrictions=('Tutorial', )),
        'skill_unlocks_on_max':
        TunableList(
            description=
            '\n            A list of skills that become unlocked when this skill is maxed.\n            ',
            tunable=TunableReference(
                description=
                '\n                A skill to unlock.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.STATISTIC),
                class_restrictions=('Skill', )))
    }
    REMOVE_INSTANCE_TUNABLES = ('min_value_tuning', 'max_value_tuning',
                                'decay_rate', '_default_convergence_value')

    def __init__(self, tracker):
        super().__init__(tracker, self.initial_value)
        self._delta_enabled = True
        self._callback_handle = None
        if self.tracker.owner.is_simulating:
            self.on_initial_startup()
        self._max_level_update_sent = False

    def on_initial_startup(self):
        if self.tracker.owner.is_selectable:
            self.refresh_level_up_callback()

    def on_remove(self, on_destroy=False):
        super().on_remove(on_destroy=on_destroy)
        self._destory_callback_handle()

    def _apply_multipliers_to_continuous_statistics(self):
        for stat in self.statistic_multipliers:
            while stat.continuous:
                owner_stat = self.tracker.get_statistic(stat)
                if owner_stat is not None:
                    owner_stat._recalculate_modified_decay_rate()

    @caches.cached
    def get_user_value(self):
        return super(Skill, self).get_user_value()

    def set_value(self,
                  value,
                  *args,
                  from_load=False,
                  interaction=None,
                  **kwargs):
        old_value = self.get_value()
        super().set_value(value, *args, **kwargs)
        self.get_user_value.cache.clear()
        if not from_load:
            new_value = self.get_value()
            new_level = self.convert_to_user_value(value)
            if old_value == self.initial_value and old_value != new_value:
                sim_info = self._tracker._owner
                services.get_event_manager().process_event(
                    test_events.TestEvent.SkillLevelChange,
                    sim_info=sim_info,
                    statistic=self.stat_type)
            old_level = self.convert_to_user_value(old_value)
            if old_level < new_level:
                self._apply_multipliers_to_continuous_statistics()

    def add_value(self, add_amount, interaction=None, **kwargs):
        old_value = self.get_value()
        if old_value == self.initial_value:
            telemhook = TELEMETRY_HOOK_SKILL_INTERACTION_FIRST_TIME
        else:
            telemhook = TELEMETRY_HOOK_SKILL_INTERACTION
        super().add_value(add_amount, interaction=interaction)
        self.get_user_value.cache.clear()
        if interaction is not None:
            self.on_skill_updated(telemhook, old_value, self.get_value(),
                                  interaction.affordance.__name__)

    def _update_value(self):
        old_value = self._value
        if gsi_handlers.sim_handlers_log.skill_change_archiver.enabled:
            last_update = self._last_update
        time_delta = super()._update_value()
        self.get_user_value.cache.clear()
        new_value = self._value
        if old_value == self.initial_value:
            telemhook = TELEMETRY_HOOK_SKILL_INTERACTION_FIRST_TIME
            self.on_skill_updated(telemhook, old_value, new_value,
                                  TELEMETRY_INTERACTION_NOT_AVAILABLE)
            sim_info = self._tracker._owner
            services.get_event_manager().process_event(
                test_events.TestEvent.SkillLevelChange,
                sim_info=sim_info,
                statistic=self.stat_type)
        old_level = self.convert_to_user_value(old_value)
        new_level = self.convert_to_user_value(new_value)
        if gsi_handlers.sim_handlers_log.skill_change_archiver.enabled and self.tracker.owner.is_sim:
            gsi_handlers.sim_handlers_log.archive_skill_change(
                self.tracker.owner, self, time_delta, old_value, new_value,
                new_level, last_update)
        if old_value < new_value and old_level < new_level:
            if self._tracker is not None:
                self._tracker.notify_watchers(self.stat_type, self._value,
                                              self._value)

    def on_skill_updated(self, telemhook, old_value, new_value,
                         affordance_name):
        owner_sim = self._tracker._owner
        if owner_sim.is_selectable:
            with telemetry_helper.begin_hook(skill_telemetry_writer,
                                             telemhook,
                                             sim=owner_sim) as hook:
                hook.write_guid(TELEMETRY_FIELD_SKILL_ID, self.guid64)
                hook.write_string(TELEMETRY_FIELD_SKILL_AFFORDANCE,
                                  affordance_name)
                hook.write_bool(TELEMETRY_FIELD_SKILL_AFFORDANCE_SUCCESS, True)
                hook.write_int(TELEMETRY_FIELD_SKILL_AFFORDANCE_VALUE_ADD,
                               new_value - old_value)
        if old_value == self.initial_value:
            skill_level = self.convert_to_user_value(old_value)
            self._show_level_notification(skill_level)

    def _destory_callback_handle(self):
        if self._callback_handle is not None:
            self.remove_callback(self._callback_handle)
            self._callback_handle = None

    def refresh_level_up_callback(self):
        self._destory_callback_handle()

        def _on_level_up_callback(stat_inst):
            new_level = stat_inst.get_user_value()
            old_level = new_level - 1
            stat_inst.on_skill_level_up(old_level, new_level)
            stat_inst.refresh_level_up_callback()

        self._callback_handle = self.add_callback(
            Threshold(self._get_next_level_bound(), operator.ge),
            _on_level_up_callback)

    def on_skill_level_up(self, old_level, new_level):
        tracker = self.tracker
        sim_info = tracker._owner
        if self.reached_max_level:
            for skill in self.skill_unlocks_on_max:
                skill_instance = tracker.add_statistic(skill, force_add=True)
                skill_instance.set_value(skill.initial_value)
        with telemetry_helper.begin_hook(skill_telemetry_writer,
                                         TELEMETRY_HOOK_SKILL_LEVEL_UP,
                                         sim=sim_info) as hook:
            hook.write_guid(TELEMETRY_FIELD_SKILL_ID, self.guid64)
            hook.write_int(TELEMETRY_FIELD_SKILL_LEVEL, new_level)
        if sim_info.account is not None:
            services.social_service.post_skill_message(sim_info, self,
                                                       old_level, new_level)
        self._show_level_notification(new_level)
        services.get_event_manager().process_event(
            test_events.TestEvent.SkillLevelChange,
            sim_info=sim_info,
            statistic=self.stat_type)

    def _show_level_notification(self, skill_level):
        sim_info = self._tracker._owner
        if not sim_info.is_npc:
            level_data = self.level_data.get(skill_level)
            if level_data is not None:
                tutorial_id = None
                if self.tutorial is not None and skill_level == 1:
                    tutorial_id = self.tutorial.guid64
                notification = level_data.level_up_notification(
                    sim_info, resolver=SingleSimResolver(sim_info))
                notification.show_dialog(icon_override=(self.icon, None),
                                         secondary_icon_override=(None,
                                                                  sim_info),
                                         additional_tokens=(skill_level, ),
                                         tutorial_id=tutorial_id)
                if level_data.level_up_screen_slam is not None:
                    level_data.level_up_screen_slam.send_screen_slam_message(
                        sim_info, sim_info, self.stat_name, skill_level)

    @classproperty
    def skill_type(cls):
        return cls

    @classproperty
    def remove_on_convergence(cls):
        return False

    @classmethod
    def can_add(cls, owner, force_add=False, **kwargs):
        if force_add:
            return True
        if cls.genders and owner.gender not in cls.genders:
            return False
        if cls.ages and owner.age not in cls.ages:
            return False
        if cls.entitlement is None:
            return True
        if owner.is_npc:
            return False
        return mtx.has_entitlement(cls.entitlement)

    @classmethod
    def get_level_list(cls):
        return cls.SKILL_LEVEL_LIST.get(cls.skill_level_type)

    @classmethod
    def get_max_skill_value(cls):
        level_list = cls.get_level_list()
        return sum(level_list)

    @classmethod
    def get_skill_value_for_level(cls, level):
        level_list = cls.get_level_list()
        if level > len(level_list):
            logger.error('Level {} out of bounds', level)
            return 0
        return sum(level_list[:level])

    @classmethod
    def get_skill_effectiveness_points_gain(cls, effectiveness_level, level):
        skill_gain_curve = cls.SKILL_EFFECTIVENESS_GAIN.get(
            effectiveness_level)
        if skill_gain_curve is not None:
            return skill_gain_curve.get(level)
        logger.error('{} does not exist in SKILL_EFFECTIVENESS_GAIN mapping',
                     effectiveness_level)
        return 0

    @classmethod
    def _tuning_loaded_callback(cls):
        super()._tuning_loaded_callback()
        level_list = cls.get_level_list()
        cls.max_level = len(level_list)
        cls.min_value_tuning = 0
        cls.max_value_tuning = sum(level_list)
        cls._default_convergence_value = cls.min_value_tuning
        cls._build_utility_curve_from_tuning_data(cls.ad_data)
        for stat in cls.statistic_multipliers:
            multiplier = cls.statistic_multipliers[stat]
            curve = multiplier.curve
            direction = multiplier.direction
            use_effective_skill = multiplier.use_effective_skill
            stat.add_skill_based_statistic_multiplier(cls, curve, direction,
                                                      use_effective_skill)
        for multiplier in cls.success_chance_multipliers:
            curve = multiplier.curve
            use_effective_skill = multiplier.use_effective_skill
            for affordance in multiplier.affordance_list:
                affordance.add_skill_multiplier(
                    affordance.success_chance_multipliers, cls, curve,
                    use_effective_skill)
        for multiplier in cls.monetary_payout_multipliers:
            curve = multiplier.curve
            use_effective_skill = multiplier.use_effective_skill
            for affordance in multiplier.affordance_list:
                affordance.add_skill_multiplier(
                    affordance.monetary_payout_multipliers, cls, curve,
                    use_effective_skill)

    @classmethod
    def _verify_tuning_callback(cls):
        success_multiplier_affordances = []
        for multiplier in cls.success_chance_multipliers:
            success_multiplier_affordances.extend(multiplier.affordance_list)
        if len(success_multiplier_affordances) != len(
                set(success_multiplier_affordances)):
            logger.error(
                "The same affordance has been tuned more than once under {}'s success multipliers, and they will overwrite each other. Please fix in tuning.",
                cls,
                owner='tastle')
        monetary_payout_multiplier_affordances = []
        for multiplier in cls.monetary_payout_multipliers:
            monetary_payout_multiplier_affordances.extend(
                multiplier.affordance_list)
        if len(monetary_payout_multiplier_affordances) != len(
                set(monetary_payout_multiplier_affordances)):
            logger.error(
                "The same affordance has been tuned more than once under {}'s monetary payout multipliers, and they will overwrite each other. Please fix in tuning.",
                cls,
                owner='tastle')

    @classmethod
    def convert_to_user_value(cls, value):
        if not cls.get_level_list():
            return 0
        current_value = value
        for (level, level_threshold) in enumerate(cls.get_level_list()):
            current_value -= level_threshold
            while current_value < 0:
                return level
        return level + 1

    @classmethod
    def convert_from_user_value(cls, user_value):
        (level_min, _) = cls._get_level_bounds(user_value)
        return level_min

    @classmethod
    def _get_level_bounds(cls, level):
        level_list = cls.get_level_list()
        level_min = sum(level_list[:level])
        if level < cls.max_level:
            level_max = sum(level_list[:level + 1])
        else:
            level_max = sum(level_list)
        return (level_min, level_max)

    def _get_next_level_bound(self):
        level = self.convert_to_user_value(self._value)
        (_, level_max) = self._get_level_bounds(level)
        return level_max

    @property
    def reached_max_level(self):
        max_value = self.get_max_skill_value()
        if self.get_value() >= max_value:
            return True
        return False

    @property
    def should_send_update(self):
        if not self.reached_max_level:
            return True
        if not self._max_level_update_sent:
            self._max_level_update_sent = True
            return True
        return False

    @classproperty
    def is_skill(cls):
        return True

    @classproperty
    def autonomy_weight(cls):
        return cls.weight

    @classmethod
    def create_skill_update_msg(cls, sim_id, stat_value):
        if not cls.convert_to_user_value(stat_value) > 0:
            return
        skill_msg = Commodities_pb2.Skill_Update()
        skill_msg.skill_id = cls.guid64
        skill_msg.curr_points = int(stat_value)
        skill_msg.sim_id = sim_id
        return skill_msg

    @property
    def is_initial_value(self):
        return self.initial_value == self.get_value()

    @classproperty
    def valid_for_stat_testing(cls):
        return True
Exemple #30
0
class Reward(HasTunableReference, metaclass=HashedTunedInstanceMetaclass, manager=services.get_instance_manager(sims4.resources.Types.REWARD)):
    __qualname__ = 'Reward'
    INSTANCE_SUBCLASSES_ONLY = True
    INSTANCE_TUNABLES = {'name': TunableLocalizedString(description='\n            The display name for this reward.\n            ', export_modes=ExportModes.All), 'reward_description': TunableLocalizedString(description='\n            Description for this reward.\n            ', export_modes=ExportModes.All), 'icon': TunableResourceKey(description='\n            The icon image for this reward.\n            ', default='PNG:missing_image', resource_types=sims4.resources.CompoundTypes.IMAGE, export_modes=ExportModes.All), 'tests': TunableTestSet(description='\n            A series of tests that must pass in order for reward to be available.\n            '), 'rewards': TunableList(TunableVariant(description='\n                The gifts that will be given for this reward. They can be either\n                a specific reward or a random reward, in the form of a list of\n                specific rewards.\n                ', specific_reward=TunableSpecificReward(), random_reward=TunableList(TunableRandomReward()))), 'notification': OptionalTunable(description='\n            If enabled, this notification will show when the sim/household receives this reward.\n            ', tunable=TunableUiDialogNotificationSnippet())}

    @classmethod
    def give_reward(cls, sim_info):
        raise NotImplementedError

    @classmethod
    def try_show_notification(cls, sim_info):
        if cls.notification is not None:
            dialog = cls.notification(sim_info, SingleSimResolver(sim_info))
            dialog.show_dialog()

    @classmethod
    def is_valid(cls, sim_info):
        if not cls.tests.run_tests(SingleSimResolver(sim_info)):
            return False
        for reward in cls.rewards:
            if not isinstance(reward, tuple):
                return reward.valid_reward(sim_info)
            for each_reward in reward:
                while not each_reward.reward.valid_reward(sim_info):
                    return False
            return True