Пример #1
0
    class SkillTimeRamp(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {
            'skill':
            Skill.TunableReference(
                description=
                "\n                The skill that should influence the interaction's running time.\n                "
            ),
            'least_skilled_completion_time':
            Tunable(
                description=
                '\n                Number of Sim minutes it should take the least-skilled Sim to\n                reach the goal value in the worst case, that is, if the stat is\n                as far from the goal value as possible.\n                ',
                tunable_type=int,
                default=None),
            'most_skilled_completion_time':
            Tunable(
                description=
                '\n                Number of Sim minutes it should take the most-skilled Sim to\n                reach the goal value in the worst case, that is, if the stat is\n                as far from the goal value as possible.\n                ',
                tunable_type=int,
                default=None)
        }

        @property
        def stat(self):
            return self.skill

        def get_maximum_running_time(self, interaction):
            skill_level = interaction.sim.get_effective_skill_level(self.skill)
            quantized_value = self.skill.convert_from_user_value(skill_level)
            p = (self.skill.max_value - quantized_value) / (
                self.skill.max_value - self.skill.min_value)
            time = sims4.math.interpolate(self.most_skilled_completion_time,
                                          self.least_skilled_completion_time,
                                          p)
            return time
Пример #2
0
class _SimInfoSkillFixupAction(_SimInfoFixupAction):
    FACTORY_TUNABLES = {'skill': Skill.TunableReference(description='\n            The skill which will be assigned to the sim_info.\n            '), 'initial_level': Tunable(description='\n            The initial level at which to assign the skill.\n            ', tunable_type=int, default=1)}

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

    def __call__(self, sim_info):
        sim_info.commodity_tracker.set_user_value(self.skill, self.initial_level)
Пример #3
0
    class SkillTimeCurve(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {
            'skill':
            Skill.TunableReference(
                description=
                "\n                The skill that should influence the interaction's running time.\n                "
            ),
            'curve':
            TunableCurve(
                description=
                "\n                A curve describing the relationship between a Sim's skill level\n                (x-axis) and the interaction's running time (y-axis).  The time\n                is the number of Sim minutes it should take the specified goal\n                commodity to reach the goal value in the worst case, that is, if\n                the stat is as far from the goal value as possible.\n                "
            )
        }

        @property
        def stat(self):
            return self.skill

        def get_maximum_running_time(self, interaction):
            skill_level = interaction.sim.get_effective_skill_level(self.skill)
            time = self.curve.get(skill_level)
            return time
Пример #4
0
class VetClinicTuning:
    UNIFORM_EMPLOYEE_MALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit male employee uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_EMPLOYEE_FEMALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit female employee uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    VET_CLINIC_VENUE = TunablePackSafeReference(
        description=
        '\n        This is a tunable reference to the type of this Venue.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.VENUE))
    DEFAULT_PROFIT_PER_TREATMENT_FOR_OFF_LOT_SIMULATION = TunableRange(
        description=
        '\n        This is used as the default profit for a treatment for off-lot simulation.\n        Once enough actual treatments have been performed, this value becomes \n        irrelevant and the MAX_COUNT_FOR_OFF_LOT_PROFIT_PER_TREATMENT tunable comes into use. \n        ',
        tunable_type=int,
        default=20,
        minimum=1)
    MAX_COUNT_FOR_OFF_LOT_PROFIT_PER_TREATMENT = TunableRange(
        description=
        '\n        The number of treatments to keep a running average of for the profit\n        per treatment calculations during off lot simulations.\n        ',
        tunable_type=int,
        default=10,
        minimum=2)
    VET_SKILL = Skill.TunablePackSafeReference(
        description=
        '\n        The vet skill for reference in code.  This can resolve to None\n        if the pack providing the skill is not installed, so beware.\n        '
    )
    VALUE_OF_SERVICE_AWARDS = TunableList(
        description=
        '\n        A threshold matrix that maps buffs to level of markup and vet skill.\n\n        Order is important.  The list is processed in reverse order.\n        The first threshold that passes returns the amount associated with it.\n        Because of this, optimal order is thresholds is ordered from lesser \n        to greater threshold values.\n        ',
        tunable=TunableTuple(
            description=
            '\n            A pair of markup threshold and skill threshold-to-buff list.\n            ',
            markup_threshold=TunableThreshold(
                description='The threshold at which this item will match.'),
            skill_to_buffs=TunableList(
                description=
                '\n                Mapping of skill threshold to the value of service that is applied.\n                \n                Order is important.  The list is processed in reverse order.\n                The first threshold that passes returns the amount associated with it.\n                Because of this, optimal order is thresholds is ordered from lesser \n                to greater threshold values.\n                ',
                tunable=TunableTuple(
                    description=
                    "\n                    A pair of skill threshold to the buff that will apply\n                    if this threshold is met when the patient is billed\n                    for a vet's services.\n                    ",
                    skill_range=SkillRangeTest.TunableFactory(
                        skill_range=SkillThreshold.TunableFactory(),
                        callback=set_vet_skill_on_threshold_test,
                        locked_args={
                            'subject': ParticipantType.Actor,
                            'skill': None,
                            'tooltip': None
                        }),
                    value_of_service_buff=TunableReference(
                        manager=services.get_instance_manager(Types.BUFF),
                        pack_safe=True)))),
        verify_tunable_callback=verify_value_of_service)
    DIFFICULTY_BONUS_PAYMENT = TunableList(
        description=
        '\n        When an NPC or player Sim treats an NPC Sim, they can get a difficulty\n        bonus depending on the difficulty of the sickness (if it is the correct\n        and ideal treatment for the sickness).\n        \n        Order is important.  The list is processed in reverse order.\n        The first threshold that passes returns the amount associated with it.\n        Because of this, optimal order is thresholds is ordered from lesser \n        to greater threshold values.\n        \n        If no thresholds pass, returned bonus amount is 0.\n        ',
        tunable=TunableTuple(
            description=
            '\n            A pair of payment amount and threshold that the payment applies to.\n            ',
            bonus_amount=TunableRange(tunable_type=int, default=100,
                                      minimum=0),
            threshold=TunableThreshold()),
        verify_tunable_callback=verify_difficulty_bonuses)
class GameRules(metaclass=TunedInstanceMetaclass,
                manager=services.get_instance_manager(
                    sims4.resources.Types.GAME_RULESET)):
    __qualname__ = 'GameRules'
    INSTANCE_TUNABLES = {
        'game_name':
        TunableLocalizedStringFactory(
            description='\n            Name of the game.\n            ',
            default=1860708663),
        'teams_per_game':
        TunableInterval(
            description=
            '\n            An interval specifying the number of teams allowed per game.\n            \n            Joining Sims are put on a new team if the maximum number of teams\n            has not yet been met, otherwise they are put into the team with the\n            fewest number of players.\n            ',
            tunable_type=int,
            default_lower=2,
            default_upper=2,
            minimum=1),
        'players_per_game':
        TunableInterval(
            description=
            '\n            An interval specifying the number of players allowed per game.\n            \n            If the maximum number of players has not been met, Sims can\n            continue to join a game.  Joining Sims are put on a new team if the\n            maximum number of teams as specified in the "teams_per_game"\n            tunable has not yet been met, otherwise they are put into the team\n            with the fewest number of players.\n            ',
            tunable_type=int,
            default_lower=2,
            default_upper=2,
            minimum=1),
        'players_per_turn':
        TunableRange(
            description=
            '\n            An integer specifying number of players from the active team who\n            take their turn at one time.\n            ',
            tunable_type=int,
            default=1,
            minimum=1),
        'initial_state':
        ObjectStateValue.TunableReference(
            description=
            "\n            The game's starting object state.\n            "),
        'score_info':
        TunableTuple(
            description=
            "\n            Tunables that affect the game's score.\n            ",
            winning_score=Tunable(
                description=
                '\n                An integer value specifying at what score the game will end.\n                ',
                tunable_type=int,
                default=100),
            score_increase=TunableInterval(
                description=
                '\n                An interval specifying the minimum and maximum score increases\n                possible in one turn. A random value in this interval will be\n                generated each time score loot is given.\n                ',
                tunable_type=int,
                default_lower=35,
                default_upper=50,
                minimum=0),
            skill_level_bonus=Tunable(
                description=
                "\n                A bonus number of points based on the Sim's skill level in the\n                relevant_skill tunable that will be added to score_increase.\n                \n                ex: If this value is 2 and the Sim receiving score has a\n                relevant skill level of 4, they will receive 8 (2 * 4) extra\n                points.\n                ",
                tunable_type=float,
                default=2),
            relevant_skill=Skill.TunableReference(
                description=
                "\n                The skill relevant to this game.  Each Sim's proficiency in\n                this skill will effect the score increase they get.\n                "
            ),
            use_effective_skill_level=Tunable(
                description=
                '\n                If checked, we will use the effective skill level rather than\n                the actual skill level of the relevant_skill tunable.\n                ',
                tunable_type=bool,
                default=True),
            progress_stat=Statistic.TunableReference(
                description=
                '\n                The statistic that advances the progress state of this game.\n                '
            )),
        'clear_score_on_player_join':
        Tunable(
            description=
            '\n            Tunable that, when checked, will clear the game score when a player joins.\n            \n            This essentially resets the game.\n            ',
            tunable_type=bool,
            default=False),
        'alternate_target_object':
        OptionalTunable(
            description=
            '\n            Tunable that, when enabled, means the game should create an alternate object\n            in the specified slot on setup that will be modified as the game goes on\n            and destroyed when the game ends.\n            ',
            tunable=TunableTuple(
                target_game_object=TunableReference(
                    description=
                    '\n                    The definition of the object that will be created/destroyed/altered\n                    by the game.\n                    ',
                    manager=services.definition_manager()),
                parent_slot=TunableVariant(
                    description=
                    '\n                    The slot on the parent object where the target_game_object object should go. This\n                    may be either the exact name of a bone on the parent object or a\n                    slot type, in which case the first empty slot of the specified type\n                    in which the child object fits will be used.\n                    ',
                    by_name=Tunable(
                        description=
                        '\n                        The exact name of a slot on the parent object in which the target\n                        game object should go.  \n                        ',
                        tunable_type=str,
                        default='_ctnm_'),
                    by_reference=TunableReference(
                        description=
                        '\n                        A particular slot type in which the target game object should go.  The\n                        first empty slot of this type found on the parent will be used.\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.SLOT_TYPE)))))
    }
Пример #6
0
class Photography:
    SMALL_PORTRAIT_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a small portrait photo.\n        ',
        manager=services.definition_manager())
    SMALL_LANDSCAPE_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a small landscape photo.\n        ',
        manager=services.definition_manager())
    MEDIUM_PORTRAIT_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a medium portrait photo.\n        ',
        manager=services.definition_manager())
    MEDIUM_LANDSCAPE_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a medium landscape photo.\n        ',
        manager=services.definition_manager())
    LARGE_PORTRAIT_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a large portrait photo.\n        ',
        manager=services.definition_manager())
    LARGE_LANDSCAPE_OBJ_DEF = TunablePackSafeReference(
        description=
        '\n        Object definition for a large landscape photo.\n        ',
        manager=services.definition_manager())
    PAINTING_INTERACTION_TAG = TunableEnumEntry(
        description=
        '\n        Tag to specify a painting interaction.\n        ',
        tunable_type=tag.Tag,
        default=tag.Tag.INVALID)
    PHOTOGRAPHY_LOOT_LIST = TunableList(
        description=
        '\n        A list of loot operations to apply to the photographer when photo mode exits.\n        ',
        tunable=TunableReference(manager=services.get_instance_manager(
            sims4.resources.Types.ACTION),
                                 class_restrictions=('LootActions', ),
                                 pack_safe=True))
    FAIL_PHOTO_QUALITY_RANGE = TunableInterval(
        description=
        '\n        The random quality statistic value that a failure photo will be\n        given between the min and max tuned values.\n        ',
        tunable_type=int,
        default_lower=0,
        default_upper=100)
    BASE_PHOTO_QUALITY_MAP = TunableMapping(
        description=
        '\n        The mapping of CameraQuality value to an interval of quality values\n        that will be used to asign a random base quality value to a photo\n        as it is created.\n        ',
        key_type=TunableEnumEntry(
            description=
            '\n            The CameraQuality value. If this photo has this CameraQuality,\n            value, then a random quality between the min value and max value\n            will be assigned to the photo.\n            ',
            tunable_type=CameraQuality,
            default=CameraQuality.CHEAP),
        value_type=TunableInterval(
            description=
            '\n            The range of base quality values from which a random value will be\n            given to the photo.\n            ',
            tunable_type=int,
            default_lower=1,
            default_upper=100))
    QUALITY_MODIFIER_PER_SKILL_LEVEL = Tunable(
        description=
        '\n        For each level of skill in Photography, this amount will be added to\n        the quality statistic.\n        ',
        tunable_type=float,
        default=0)
    PHOTO_VALUE_MODIFIER_MAP = TunableMapping(
        description=
        '\n        The mapping of state values to Simoleon value modifiers.\n        The final value of a photo is decided based on its\n        current value multiplied by the sum of all modifiers for\n        states that apply to the photo. All modifiers are\n        added together first, then the sum will be multiplied by\n        the current price.\n        ',
        key_type=TunableStateValueReference(
            description=
            '\n            The quality state values. If this photo has this state,\n            then a random modifier between min_value and max_value\n            will be multiplied to the current price.'
        ),
        value_type=TunableInterval(
            description=
            '\n            The maximum modifier multiplied to the current price based on the provided state value\n            ',
            tunable_type=float,
            default_lower=1,
            default_upper=1))
    PHOTO_VALUE_SKILL_CURVE = TunableStatisticModifierCurve.TunableFactory(
        description=
        "\n        Allows you to adjust the final value of the photo based on the Sim's\n        level of a given skill.\n        ",
        axis_name_overrides=('Skill Level', 'Simoleon Multiplier'),
        locked_args={'subject': ParticipantType.Actor})
    PHOTOGRAPHY_SKILL = Skill.TunablePackSafeReference(
        description='\n        A reference to the photography skill.\n        '
    )
    EMOTION_STATE_MAP = TunableMapping(
        description=
        "\n        The mapping of moods to states, used to give photo objects a mood\n        based state. These states are then used by the tooltip component to\n        display emotional content on the photo's tooltip.\n        ",
        key_type=TunableReference(
            description=
            '\n            The mood to associate with a state.\n            ',
            manager=services.mood_manager()),
        value_type=TunableStateValueReference(
            description=
            '\n            The state that represents the mood for the purpose of displaying\n            emotional content in a tooltip.\n            '
        ))
    PHOTO_OBJECT_LOOT_PER_TARGET = TunableList(
        description=
        '\n        A list of loots which will be applied once PER target. The participants\n        for each application will be Actor: photographer, Target: photograph\n        target and Object: the Photograph itself. If a photo interaction has 2\n        target sims, this loot will be applied twice.\n        ',
        tunable=TunableReference(manager=services.get_instance_manager(
            sims4.resources.Types.ACTION),
                                 pack_safe=True))
    MOOD_PARAM_TO_MOOD_CATEGORY_STATE = TunableMapping(
        description=
        '\n        If the player took a picture in a photo mode that supports mood\n        categories, we will perform a state change to the corresponding state\n        based on the mood that each picture was taken in.\n        ',
        key_type=Tunable(
            description=
            '\n            The mood ASM parameter value to associate with a state.\n            ',
            tunable_type=str,
            default=None),
        value_type=TunableStateValueReference(
            description=
            '\n            The state that represents the mood category.\n            '
        ))
    GROUP_PHOTO_X_ACTOR_TAG = TunableEnumEntry(
        description=
        '\n        Tag to specify the photo studio interaction that the photo target sim\n        who should be considered the x actor will run.\n        ',
        tunable_type=tag.Tag,
        default=tag.Tag.INVALID,
        invalid_enums=(tag.Tag.INVALID, ))
    GROUP_PHOTO_Y_ACTOR_TAG = TunableEnumEntry(
        description=
        '\n        Tag to specify the photo studio interaction that the photo target sim\n        who should be considered the y actor will run.\n        ',
        tunable_type=tag.Tag,
        default=tag.Tag.INVALID,
        invalid_enums=(tag.Tag.INVALID, ))
    GROUP_PHOTO_Z_ACTOR_TAG = TunableEnumEntry(
        description=
        '\n        Tag to specify the photo studio interaction that the photo target sim\n        who should be considered the z actor will run.\n        ',
        tunable_type=tag.Tag,
        default=tag.Tag.INVALID,
        invalid_enums=(tag.Tag.INVALID, ))
    NUM_PHOTOS_PER_SESSION = Tunable(
        description=
        '\n        Max possible photos that can be taken during one photo session. Once\n        this number has been reached, the photo session will exit.\n        ',
        tunable_type=int,
        default=5)

    @classmethod
    def _is_fail_photo(cls, photo_style_type):
        if photo_style_type == PhotoStyleType.EFFECT_GRAINY or (
                photo_style_type == PhotoStyleType.EFFECT_OVERSATURATED or
            (photo_style_type == PhotoStyleType.EFFECT_UNDERSATURATED or
             (photo_style_type == PhotoStyleType.PHOTO_FAIL_BLURRY or
              (photo_style_type == PhotoStyleType.PHOTO_FAIL_FINGER
               or photo_style_type == PhotoStyleType.PHOTO_FAIL_GNOME)))
        ) or photo_style_type == PhotoStyleType.PHOTO_FAIL_NOISE:
            return True
        return False

    @classmethod
    def _apply_quality_and_value_to_photo(cls, photographer_sim, photo_obj,
                                          photo_style, camera_quality):
        quality_stat = CraftingTuning.QUALITY_STATISTIC
        quality_stat_tracker = photo_obj.get_tracker(quality_stat)
        if cls._is_fail_photo(photo_style):
            final_quality = cls.FAIL_PHOTO_QUALITY_RANGE.random_int()
        else:
            quality_range = cls.BASE_PHOTO_QUALITY_MAP.get(
                camera_quality, None)
            if quality_range is None:
                logger.error(
                    'Photography tuning BASE_PHOTO_QUALITY_MAP does not have an expected quality value: []',
                    str(camera_quality))
                return
            base_quality = quality_range.random_int()
            skill_quality_modifier = 0
            if cls.PHOTOGRAPHY_SKILL is not None:
                effective_skill_level = photographer_sim.get_effective_skill_level(
                    cls.PHOTOGRAPHY_SKILL)
                if effective_skill_level:
                    skill_quality_modifier = effective_skill_level * cls.QUALITY_MODIFIER_PER_SKILL_LEVEL
            final_quality = base_quality + skill_quality_modifier
        quality_stat_tracker.set_value(quality_stat, final_quality)
        value_multiplier = 1
        for (state_value, value_mods) in cls.PHOTO_VALUE_MODIFIER_MAP.items():
            if photo_obj.has_state(state_value.state):
                actual_state_value = photo_obj.get_state(state_value.state)
                if state_value is actual_state_value:
                    value_multiplier *= value_mods.random_float()
                    break
        value_multiplier *= cls.PHOTO_VALUE_SKILL_CURVE.get_multiplier(
            SingleSimResolver(photographer_sim), photographer_sim)
        photo_obj.base_value = int(photo_obj.base_value * value_multiplier)

    @classmethod
    def _get_mood_sim_info_if_exists(cls, photographer_sim_info,
                                     target_sim_ids, camera_mode):
        if camera_mode is CameraMode.SELFIE_PHOTO:
            return photographer_sim_info
        else:
            num_target_sims = len(target_sim_ids)
            if num_target_sims == 1:
                sim_info_manager = services.sim_info_manager()
                target_sim_info = sim_info_manager.get(target_sim_ids[0])
                return target_sim_info

    @classmethod
    def _apply_mood_state_if_appropriate(cls, photographer_sim_info,
                                         target_sim_ids, camera_mode,
                                         photo_object):
        mood_sim_info = cls._get_mood_sim_info_if_exists(
            photographer_sim_info, target_sim_ids, camera_mode)
        if mood_sim_info:
            mood = mood_sim_info.get_mood()
            mood_state = cls.EMOTION_STATE_MAP.get(mood, None)
            if mood_state:
                photo_object.set_state(mood_state.state, mood_state)

    @classmethod
    def _apply_mood_category_state_if_appropriate(cls, selected_mood_param,
                                                  camera_mode, photo_object):
        if camera_mode in (CameraMode.TRIPOD, CameraMode.SIM_PHOTO,
                           CameraMode.PHOTO_STUDIO_PHOTO):
            mood_category_state = cls.MOOD_PARAM_TO_MOOD_CATEGORY_STATE.get(
                selected_mood_param, None)
            if mood_category_state:
                photo_object.set_state(mood_category_state.state,
                                       mood_category_state)

    @classmethod
    def create_photo_from_photo_data(cls, camera_mode, camera_quality,
                                     photographer_sim_id, target_obj_id,
                                     target_sim_ids, res_key, photo_style,
                                     photo_size, photo_orientation,
                                     photographer_sim_info, photographer_sim,
                                     time_stamp, selected_mood_param):
        photo_object = None
        is_paint_by_reference = camera_mode is CameraMode.PAINT_BY_REFERENCE
        if is_paint_by_reference:
            current_zone = services.current_zone()
            photo_object = current_zone.object_manager.get(target_obj_id)
            if photo_object is None:
                photo_object = current_zone.inventory_manager.get(
                    target_obj_id)
        else:
            if photo_orientation == PhotoOrientation.LANDSCAPE:
                if photo_size == PhotoSize.LARGE:
                    photo_object_def = cls.LARGE_LANDSCAPE_OBJ_DEF
                elif photo_size == PhotoSize.MEDIUM:
                    photo_object_def = cls.MEDIUM_LANDSCAPE_OBJ_DEF
                elif photo_size == PhotoSize.SMALL:
                    photo_object_def = cls.SMALL_LANDSCAPE_OBJ_DEF
            elif photo_orientation == PhotoOrientation.PORTRAIT:
                if photo_size == PhotoSize.LARGE:
                    photo_object_def = cls.LARGE_PORTRAIT_OBJ_DEF
                elif photo_size == PhotoSize.MEDIUM:
                    photo_object_def = cls.MEDIUM_PORTRAIT_OBJ_DEF
                elif photo_size == PhotoSize.SMALL:
                    photo_object_def = cls.SMALL_PORTRAIT_OBJ_DEF
                else:
                    photo_object_def = cls.SMALL_LANDSCAPE_OBJ_DEF
            if photo_object_def is None:
                return
            photo_object = create_object(photo_object_def)
        if photo_object is None:
            logger.error('photo object could not be found.')
            return
        for target_sim_id in target_sim_ids:
            target_sim_info = services.sim_info_manager().get(target_sim_id)
            target_sim = target_sim_info.get_sim_instance()
            resolver = DoubleSimAndObjectResolver(photographer_sim,
                                                  target_sim,
                                                  photo_object,
                                                  source=cls)
            for loot in cls.PHOTO_OBJECT_LOOT_PER_TARGET:
                loot.apply_to_resolver(resolver)
        photography_service = services.get_photography_service()
        loots = photography_service.get_loots_for_photo()
        for photoloot in loots:
            if photoloot._AUTO_FACTORY.FACTORY_TYPE is RotateTargetPhotoLoot:
                photographer_sim = photoloot.photographer
                photographer_sim_info = photographer_sim.sim_info
                break
        reveal_level = PaintingState.REVEAL_LEVEL_MIN if is_paint_by_reference else PaintingState.REVEAL_LEVEL_MAX
        painting_state = PaintingState.from_key(res_key, reveal_level, False,
                                                photo_style)
        photo_object.canvas_component.painting_state = painting_state
        photo_object.canvas_component.time_stamp = time_stamp
        photo_object.set_household_owner_id(photographer_sim.household_id)
        if selected_mood_param:
            cls._apply_mood_category_state_if_appropriate(
                selected_mood_param, camera_mode, photo_object)
        if not is_paint_by_reference:
            cls._apply_quality_and_value_to_photo(photographer_sim,
                                                  photo_object, photo_style,
                                                  camera_quality)
            cls._apply_mood_state_if_appropriate(photographer_sim_info,
                                                 target_sim_ids, camera_mode,
                                                 photo_object)
            photo_object.add_dynamic_component(STORED_SIM_INFO_COMPONENT,
                                               sim_id=photographer_sim.id)
            photo_object.update_object_tooltip()
            if not (photographer_sim.inventory_component.can_add(photo_object)
                    and photographer_sim.inventory_component.
                    player_try_add_object(photo_object)):
                logger.error(
                    "photo object could not be put in the sim's inventory, deleting photo."
                )
                photo_object.destroy()
        photo_targets = [
            services.sim_info_manager().get(sim_id)
            for sim_id in target_sim_ids
        ]
        if camera_mode == CameraMode.TWO_SIM_SELFIE_PHOTO:
            photo_targets.append(photographer_sim_info)
        photo_targets = frozenset(photo_targets)
        services.get_event_manager().process_event(
            test_events.TestEvent.PhotoTaken,
            sim_info=photographer_sim_info,
            photo_object=photo_object,
            photo_targets=photo_targets)
Пример #7
0
class UiRecipePicker(UiDialogObjectPicker):
    __qualname__ = 'UiRecipePicker'

    @staticmethod
    def _verify_tunable_callback(instance_class,
                                 tunable_name,
                                 source,
                                 column_sort_priorities=None,
                                 picker_columns=None,
                                 **kwargs):
        if column_sort_priorities is not None:
            length = len(picker_columns)
            if any(v >= length for v in column_sort_priorities):
                logger.error(
                    'UiRecipePicker dialog in {} has invalid column sort priority. Valid values are 0-{}',
                    instance_class,
                    length - 1,
                    owner='cjiang')

    FACTORY_TUNABLES = {
        'skill':
        OptionalTunable(
            Skill.TunableReference(
                description=
                '\n            The skill associated with the picker dialog.\n            '
            )),
        'picker_columns':
        TunableList(
            description='\n            List of the column info\n            ',
            tunable=PickerColumn.TunableFactory()),
        'column_sort_priorities':
        OptionalTunable(
            description=
            '\n            If enabled, specifies column sorting.\n            ',
            tunable=TunableList(
                description=
                '\n                The priority index for the column (column numbers are 0-based\n                index. So, if you wish to use the first column the id is 0).\n                ',
                tunable=int)),
        'verify_tunable_callback':
        _verify_tunable_callback
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.picker_type = ObjectPickerType.RECIPE

    def _customize_add_row(self, row):
        for picker_row in self.picker_rows:
            self._build_row_links(row, picker_row)
            self._build_row_links(picker_row, row)

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

    @staticmethod
    def _build_row_links(row1, row2):
        if row1.linked_recipe is not None and row1.linked_recipe is row2.tag:
            row2.linked_option_ids.append(row1.option_id)

    def _build_customize_picker(self, picker_data):
        for column in self.picker_columns:
            column_data = picker_data.recipe_picker_data.column_list.add()
            column.populate_protocol_buffer(column_data)
        if self.skill is not None:
            picker_data.recipe_picker_data.skill_id = self.skill.guid64
        if self.column_sort_priorities is not None:
            picker_data.recipe_picker_data.column_sort_list.extend(
                self.column_sort_priorities)
        for row in self.picker_rows:
            row_data = picker_data.recipe_picker_data.row_data.add()
            row.populate_protocol_buffer(row_data)
Пример #8
0
class GameRules(HasTunableReference,
                metaclass=TunedInstanceMetaclass,
                manager=services.get_instance_manager(
                    sims4.resources.Types.GAME_RULESET)):
    ENDING_CONDITION_SCORE = 0
    ENDING_CONDITION_ROUND = 1
    INSTANCE_TUNABLES = {
        'game_name':
        TunableLocalizedStringFactory(
            description='\n            Name of the game.\n            ',
            default=1860708663),
        'team_strategy':
        TunableVariant(
            description=
            '\n            Define how Sims are distributed across teams.\n            ',
            auto_balanced=GameTeamAutoBalanced.TunableFactory(),
            part_driven=GameTeamPartDriven.TunableFactory(),
            default='auto_balanced'),
        'teams_per_game':
        TunableInterval(
            description=
            '\n            An interval specifying the number of teams allowed per game.\n            \n            Joining Sims are put on a new team if the maximum number of teams\n            has not yet been met, otherwise they are put into the team with the\n            fewest number of players.\n            ',
            tunable_type=int,
            default_lower=2,
            default_upper=2,
            minimum=1),
        'players_per_game':
        TunableInterval(
            description=
            '\n            An interval specifying the number of players allowed per game.\n            \n            If the maximum number of players has not been met, Sims can\n            continue to join a game.  Joining Sims are put on a new team if the\n            maximum number of teams as specified in the "teams_per_game"\n            tunable has not yet been met, otherwise they are put into the team\n            with the fewest number of players.\n            ',
            tunable_type=int,
            default_lower=2,
            default_upper=2,
            minimum=1),
        'players_per_turn':
        TunableRange(
            description=
            '\n            An integer specifying number of players from the active team who\n            take their turn at one time.\n            ',
            tunable_type=int,
            default=1,
            minimum=1),
        'initial_state':
        ObjectStateValue.TunableReference(
            description=
            "\n            The game's starting object state.\n            ",
            allow_none=True),
        'score_info':
        TunableTuple(
            description=
            "\n            Tunables that affect the game's score.\n            ",
            ending_condition=TunableVariant(
                description=
                '\n                The condition under which the game ends.\n                ',
                score_based=TunableTuple(
                    description=
                    '\n                    A game that ends when one of the teams wins by reaching a \n                    certain score first\n                    ',
                    locked_args={'end_condition': ENDING_CONDITION_SCORE},
                    winning_score=Tunable(
                        description=
                        '\n                        Score required to win.\n                        ',
                        tunable_type=int,
                        default=100)),
                round_based=TunableTuple(
                    description=
                    '\n                    A game that ends after a certain number of rounds.  The Team\n                    with the highest score at that point wins.\n                    ',
                    locked_args={'end_condition': ENDING_CONDITION_ROUND},
                    rounds=Tunable(
                        description=
                        '\n                        Length of game (in rounds).\n                        ',
                        tunable_type=int,
                        default=3)),
                default='score_based'),
            score_increase=TunableInterval(
                description=
                '\n                An interval specifying the minimum and maximum score increases\n                possible in one turn. A random value in this interval will be\n                generated each time score loot is given.\n                ',
                tunable_type=int,
                default_lower=35,
                default_upper=50,
                minimum=0),
            allow_scoring_for_non_active_teams=Tunable(
                description=
                '\n                If checked, any Sim may score, even if their team is not\n                considered active.\n                ',
                tunable_type=bool,
                default=False),
            skill_level_bonus=Tunable(
                description=
                "\n                A bonus number of points based on the Sim's skill level in the\n                relevant_skill tunable that will be added to score_increase.\n                \n                ex: If this value is 2 and the Sim receiving score has a\n                relevant skill level of 4, they will receive 8 (2 * 4) extra\n                points.\n                ",
                tunable_type=float,
                default=2),
            relevant_skill=Skill.TunableReference(
                description=
                "\n                The skill relevant to this game.  Each Sim's proficiency in\n                this skill will effect the score increase they get.\n                ",
                allow_none=True),
            use_effective_skill_level=Tunable(
                description=
                '\n                If checked, we will use the effective skill level rather than\n                the actual skill level of the relevant_skill tunable.\n                ',
                tunable_type=bool,
                default=True),
            progress_stat=Statistic.TunableReference(
                description=
                '\n                The statistic that advances the progress state of this game.\n                ',
                allow_none=True),
            persist_high_score=Tunable(
                description=
                '\n                If checked, the high score and the team Sim ids will be\n                saved onto the game component.\n                ',
                tunable_type=bool,
                default=False)),
        'clear_score_on_player_join':
        Tunable(
            description=
            '\n            Tunable that, when checked, will clear the game score when a player joins.\n            \n            This essentially resets the game.\n            ',
            tunable_type=bool,
            default=False),
        'alternate_target_object':
        OptionalTunable(
            description=
            '\n            Tunable that, when enabled, means the game should create an alternate object\n            in the specified slot on setup that will be modified as the game goes on\n            and destroyed when the game ends.\n            ',
            tunable=TunableTuple(
                target_game_object=TunableReference(
                    description=
                    '\n                    The definition of the object that will be created/destroyed/altered\n                    by the game.\n                    ',
                    manager=services.definition_manager()),
                parent_slot=TunableVariant(
                    description=
                    '\n                    The slot on the parent object where the target_game_object object should go. This\n                    may be either the exact name of a bone on the parent object or a\n                    slot type, in which case the first empty slot of the specified type\n                    in which the child object fits will be used.\n                    ',
                    by_name=Tunable(
                        description=
                        '\n                        The exact name of a slot on the parent object in which the target\n                        game object should go.  \n                        ',
                        tunable_type=str,
                        default='_ctnm_'),
                    by_reference=TunableReference(
                        description=
                        '\n                        A particular slot type in which the target game object should go.  The\n                        first empty slot of this type found on the parent will be used.\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.SLOT_TYPE))),
                destroy_at_end=Tunable(
                    description=
                    '\n                    If True, the alternate target object will get destroyed at the end of the game.\n                    ',
                    tunable_type=bool,
                    default=True))),
        'game_over_notification':
        OptionalTunable(
            description=
            "\n            If enabled, when any Sim involved in the game is a player-controlled\n            Sim, display a notification when the game is over.\n            \n            NOTE: As of now, this only triggers when there are *exactly* two\n            teams. To support more teams, we'll need to extend the possible\n            string permutation.\n            ",
            tunable=TunableTuple(
                one_v_one=TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when the game is 1v1.\n                    \n                     * Token 0 is the object the game is being played on\n                     * Token 1 is the winner\n                     * Token 2 is the loser\n                     * Token 3 is the winner's score\n                     * Token 4 is the loser's score\n                    "
                ),
                one_v_many_winner
                =TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when the game is 1 v many, and the\n                    single Sim is the winner.\n                    \n                    * Token 0 is the object the game is being played on\n                    * Token 1 is the winner\n                    * Token 2 is a list of losers (Alice, Bob, and Carol)\n                    * Token 3 is the winner's score\n                    * Token 4 is the loser's score\n                    "
                ),
                one_v_many_loser=TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when the game is 1 v many, and the\n                    single Sim is the loser.\n                    \n                    * Token 0 is the object the game is being played on\n                    * Token 1 is a list of winners (Alice, Bob, and Carol)\n                    * Token 2 is the loser\n                    * Token 3 is the winner's score\n                    * Token 4 is the loser's score\n                    "
                ),
                many_v_many=TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when the game is many v many.\n                    \n                    * Token 0 is the object the game is being played on\n                    * Token 1 is a list of winners (Alice and Bob)\n                    * Token 2 is a list of losers (Carol, Dan, and Erin)\n                    * Token 3 is the winner's score\n                    * Token 4 is the loser's score\n                    "
                ))),
        'game_over_winner_only_notification':
        OptionalTunable(
            description=
            '\n            If enabled, when any Sim involved in the game is a player-controlled\n            Sim, display a notification when the game is over.\n            \n            NOTE: This will show only the winners of the game with the highest \n            score. The winners can be more than one team if they have same \n            score.\n            ',
            tunable=TunableTuple(
                play_alone=TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when Sim play alone.\n                    \n                    * Token 0 is the object the game is being played on\n                    * Token 1 is the Sim's name\n                    * Token 2 is the Sim's score\n                    "
                ),
                winner=TunableUiDialogNotificationSnippet(
                    description=
                    "\n                    The notification to show when the game has 1 team winner.\n                    \n                    * Token 0 is the object the game is being played on\n                    * Token 1 is the winner\n                    * Token 2 is the winner's score\n                    "
                ))),
        'additional_game_behavior':
        OptionalTunable(
            description=
            '\n            If enabled additional behavior will be run for this type of game\n            on multiple phases like creating destroying additional objects on \n            setup of end phases.\n            ',
            tunable=TunableVariant(
                description=
                "\n                Variant of type of games that will add very specific behavior\n                to the game component.\n                e.g. Card battle behavior will create cards and destroy them\n                depending on each actor's inventory.\n                ",
                card_battle=CardBattleBehavior.TunableFactory(),
                create_object=CreateObjectBehavior.TunableFactory(),
                default='card_battle'))
    }

    def __init__(self, game_component, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._game_component = game_component
        self.additional_game_behavior = self.additional_game_behavior(
        ) if self.additional_game_behavior is not None else None

    def add_player(self, sim):
        self.team_strategy.add_player(self._game_component, sim)

    @classmethod
    def can_be_on_same_team(cls, target_a, target_b):
        return cls.team_strategy.can_be_on_same_team(target_a, target_b)

    @classmethod
    def team_determines_part(cls):
        return cls.team_strategy.team_determines_part()

    @classmethod
    def can_be_on_opposing_team(cls, target_a, target_b):
        return cls.team_strategy.can_be_on_opposing_team(target_a, target_b)

    def remove_player(self, sim):
        self.team_strategy.remove_player(self._game_component, sim)
Пример #9
0
class EffectiveSkillModifier(HasTunableSingletonFactory, BaseGameEffectModifier):
    __qualname__ = 'EffectiveSkillModifier'
    FACTORY_TUNABLES = {'description': '\n        The modifier to change the effective skill or skill_tag tuned in the\n        modifier key The value of the modifier can be negative..\n        ', 'modifier_key': TunableVariant(description='\n            ', skill_type=Skill.TunableReference(description='\n                            What skill to apply the modifier on.'), skill_tag=TunableEnumEntry(description='\n                            What skill tag to apply the modifier on.', tunable_type=tag.Tag, default=tag.Tag.INVALID)), 'modifier_value': Tunable(description='\n            The value to change the effective skill. Can be negative.', tunable_type=int, default=0)}

    def __init__(self, modifier_key, modifier_value, **kwargs):
        BaseGameEffectModifier.__init__(self, GameEffectType.EFFECTIVE_SKILL_MODIFIER)
        self.modifier_key = modifier_key
        self.modifier_value = modifier_value

    def can_modify(self, skill):
        if self.modifier_key is skill.skill_type:
            return True
        return self.modifier_key in skill.tags

    def get_modifier_value(self, skill):
        if self.can_modify(skill):
            return self.modifier_value
        return 0
Пример #10
0
class SkillBasedMultiInteraction(SocialSuperInteraction):
    INSTANCE_TUNABLES = {
        'skill_interactions':
        TunableList(
            description=
            '\n            For each item in this list, if the actor has the skill, and the\n            object criteria test passes, a new interaction will be generated on\n            the Sim.\n            ',
            tunable=TunableTuple(
                description=
                '\n                If the actor has the skill, and the object criteria test passes,\n                a new interaction will be generated on the Sim.\n                ',
                skill=Skill.TunablePackSafeReference(
                    description=
                    '\n                    If the actor of the interaction has this skill, the Interaction Data will be\n                    used to generate interactions on the actor.\n                    '
                ),
                object_criteria=OptionalTunable(
                    ObjectCriteriaAndSpecificTests.TunableFactory(
                        description=
                        '\n                    If enabled, the object criteria test must also pass for the\n                    Interaction Data to generate interactions on the actor. \n                    '
                    )),
                interaction_data=TunableList(
                    TunableTuple(
                        description=
                        '\n                    The data used to generate interactions on the actor.,\n                    ',
                        interaction_name=TunableLocalizedStringFactory(
                            description=
                            '\n                        The name given to the generated interaction.\n                        '
                        ),
                        outcome=TunableOutcome(
                            description=
                            '\n                        The outcome to use for the generated interaction.\n                        '
                        )))),
            tuning_group=GroupNames.CORE)
    }

    def __init__(self,
                 *args,
                 display_name_override=None,
                 outcome_override=None,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self._display_name_override = display_name_override
        self._outcome_override = outcome_override

    @classmethod
    def potential_interactions(cls, target, context, **kwargs):
        resolver = SingleSimResolver(context.sim)
        for skill_interaction in cls.skill_interactions:
            skill_tracker = target.get_tracker(skill_interaction.skill)
            if skill_tracker is None:
                continue
            skill_stat = skill_tracker.get_statistic(skill_interaction.skill,
                                                     add=False)
            if skill_stat is None:
                continue
            if not not skill_interaction.object_criteria is not None and not not resolver(
                    skill_interaction.object_criteria):
                continue
            for interaction_datum in skill_interaction.interaction_data:
                yield AffordanceObjectPair(
                    cls,
                    target,
                    cls,
                    None,
                    display_name_override=interaction_datum.interaction_name,
                    outcome_override=interaction_datum.outcome,
                    **kwargs)

    def _build_outcome_sequence(self):
        if self._outcome_override is not None:
            return self._outcome_override.build_elements(
                self, update_global_outcome_result=True)

    @flexmethod
    def _get_name(cls,
                  inst,
                  target=DEFAULT,
                  context=DEFAULT,
                  display_name_override=None,
                  **interaction_parameters):
        if display_name_override is not None:
            return display_name_override()
        return super(SuperInteraction,
                     inst if inst is not None else cls)._get_name(
                         target=target,
                         context=context,
                         **interaction_parameters)
Пример #11
0
class SkillRangeTest(HasTunableSingletonFactory, AutoFactoryInit,
                     event_testing.test_base.BaseTest):
    FACTORY_TUNABLES = {
        'subject':
        TunableEnumEntry(
            description='\n            The subject of this test.\n            ',
            tunable_type=ParticipantType,
            default=ParticipantType.Actor),
        'skill':
        Skill.TunablePackSafeReference(
            description=
            '\n            The skill to test against. \n            \n            Should the Sim not have the specified skill, or should the skill not\n            be available because of pack restrictions, this Sim will be\n            considered at level 0.\n            '
        ),
        'skill_range':
        TunableVariant(
            description=
            '\n            A skill range defined by either an interval or a threshold.\n            ',
            interval=SkillInterval.TunableFactory(),
            threshold=SkillThreshold.TunableFactory(),
            default='interval'),
        'use_effective_skill_level':
        Tunable(
            description=
            "\n            If checked, then instead of using the skill's actual level, the test\n            will use the skill's effective level for the purpose of satisfying\n            the specified criteria.\n            ",
            tunable_type=bool,
            needs_tuning=True,
            default=False)
    }
    __slots__ = ('subject', 'skill', 'skill_range',
                 'use_effective_skill_level')

    def get_expected_args(self):
        return {'test_targets': self.subject}

    @property
    def skill_range_min(self):
        return self.skill_range.skill_range_min

    @property
    def skill_range_max(self):
        max_possible_level = self.skill.get_max_skill_value()
        range_max = self.skill_range.skill_range_max
        if range_max > max_possible_level:
            logger.error(
                "SkillRangeTest has a tuned skill range upper bound of {} that is higher than {}'s highest level of {}.",
                self.skill,
                range_max,
                max_possible_level,
                owner='rmccord')
        return range_max

    @cached_test
    def __call__(self, test_targets=()):
        for target in test_targets:
            if self.skill is None:
                skill_value = 0
            else:
                skill_or_skill_type = target.get_statistic(
                    self.skill, add=False) or self.skill
                if self.use_effective_skill_level and target.is_instanced():
                    skill_value = target.get_sim_instance(
                        allow_hidden_flags=ALL_HIDDEN_REASONS
                    ).get_effective_skill_level(skill_or_skill_type)
                else:
                    skill_value = skill_or_skill_type.get_user_value()
            if not self.skill_range(skill_value):
                return TestResult(False,
                                  'skill level not in desired range.',
                                  tooltip=self.tooltip)
            return TestResult.TRUE
        return TestResult(False,
                          'Sim does not have required skill.',
                          tooltip=self.tooltip)