Example #1
0
class WeatherForecastOverrideTest(HasTunableSingletonFactory, AutoFactoryInit,
                                  BaseTest):
    FACTORY_TUNABLES = {
        'weather_forecasts':
        TunableWhiteBlackList(
            description=
            '\n            Black/White lists of the forecasts to test against\n            ',
            tunable=TunableWeatherSeasonalForecastsReference())
    }

    def get_expected_args(self):
        return {}

    @cached_test
    def __call__(self):
        weather_service = services.weather_service()
        if weather_service is not None:
            override_forecast = weather_service.get_override_forecast()
        else:
            override_forecast = None
        if not self.weather_forecasts.test_item(override_forecast):
            return TestResult(
                False,
                "Current override forecast {} doesn't pass white/black list",
                override_forecast,
                tooltip=self.tooltip)
        return TestResult.TRUE
class BaseCivicPolicyTestBWList(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {
        'white_black_list':
        TunableWhiteBlackList(
            description=
            '\n            The civic policies must pass the whitelist and blacklist\n            to pass the test.\n            ',
            tunable=TunableReference(
                description=
                '\n                Allowed and disallowed civic policies.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.SNIPPET),
                class_restrictions=('BaseCivicPolicy', ),
                pack_safe=True))
    }

    def get_custom_event_keys(self, provider_owner):
        return []

    def get_civic_policy_tuning_test_list(self, provider):
        raise NotImplementedError

    def run_test(self, provider, tooltip):
        civic_policies = (
        ) if provider is None else self.get_civic_policy_tuning_test_list(
            provider)
        if not self.white_black_list.test_collection(civic_policies):
            return TestResult(
                False,
                'Civic Policy Provider failed enacted white or black list {}',
                civic_policies,
                tooltip=tooltip)
        return TestResult.TRUE
Example #3
0
    class _SpecificMajors(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {'_majors': TunableWhiteBlackList(description="\n                The sim's enrolled major must match against the whitelist and blacklist\n                to pass.\n                ", tunable=TunableReference(description='\n                    A University major to test against.\n                    ', manager=services.get_instance_manager(sims4.resources.Types.UNIVERSITY_MAJOR)))}

        def is_valid_major(self, sim_info, tooltip=None):
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is None:
                return TestResult(False, '{0} has no degree tracker.', sim_info, tooltip=tooltip)
            major = degree_tracker.get_major()
            if not self._majors.test_item(major):
                return TestResult(False, "{0}'s enrolled majors do not match the tuned whitelist/blacklist.", sim_info, tooltip=tooltip)
            return TestResult.TRUE
Example #4
0
class BusinessSettingTest(HasTunableSingletonFactory, AutoFactoryInit,
                          test_base.BaseTest):
    FACTORY_TUNABLES = {
        'quality_setting':
        OptionalTunable(
            description=
            '\n            A test to see if the current business has certain settings set by the owner.\n            ',
            tunable=TunableWhiteBlackList(
                description=
                '\n                Use of this white/black list will check whether or not the \n                current on-lot business is set to certain quality settings.\n                ',
                tunable=TunableEnumEntry(
                    description=
                    '\n                    Business Quality Type from business settings.\n                    ',
                    tunable_type=BusinessQualityType,
                    default=BusinessQualityType.INVALID,
                    invalid_enums=(BusinessQualityType.INVALID, ))),
            disabled_name='ignore',
            enabled_name='test'),
        'star_rating':
        OptionalTunable(
            description=
            '\n            A test to see if the current business is within a star rating range.\n            ',
            tunable=TunableInterval(
                description=
                "\n                If the business's star rating is within this range, this test passes.\n                ",
                tunable_type=float,
                default_lower=0,
                default_upper=5,
                minimum=0),
            disabled_name='ignore',
            enabled_name='test')
    }

    def get_expected_args(self):
        return {}

    def __call__(self):
        business_manager = services.business_service(
        ).get_business_manager_for_zone()
        if business_manager is None:
            return TestResult(False, 'Not currently on a business lot.')
        if self.quality_setting is not None and not self.quality_setting.test_item(
                business_manager.quality_setting):
            return TestResult(
                False, 'Business is set to {}'.format(
                    business_manager.quality_setting))
        if self.star_rating is not None:
            business_star_rating = business_manager.get_star_rating()
            if business_star_rating not in self.star_rating:
                return TestResult(
                    False,
                    'Business star rating is {}'.format(business_star_rating))
        return TestResult.TRUE
Example #5
0
class _ActiveNarrativeTest(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {
        'narratives':
        TunableWhiteBlackList(tunable=TunableReference(
            manager=services.get_instance_manager(Types.NARRATIVE)))
    }

    def test(self, tooltip):
        if self.narratives.test_collection(
                services.narrative_service().active_narratives):
            return TestResult.TRUE
        return TestResult(False,
                          'Failed to pass narrative white/black list.',
                          tooltip=tooltip)
Example #6
0
class FormationCompatibility(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {'compatibility': TunableWhiteBlackList(description='\n            A white/blacklist that determines compatibility via\n            required or prohibited formations.\n            ', tunable=TunableReference(description='\n                A routing formation\n                ', manager=services.get_instance_manager(sims4.resources.Types.SNIPPET), class_restrictions=('RoutingFormation',), pack_safe=True))}

    def test(self, master, slave, tooltip):
        test_formations = None
        if master is not None:
            test_formations = set(master.get_all_routing_slave_data_gen())
        if slave is not None:
            if slave.routing_master is None:
                test_formations = (None,)
            elif test_formations is not None:
                test_formations &= {slave.routing_master.get_formation_data_for_slave(slave)}
            else:
                test_formations = (slave.routing_master.get_formation_data_for_slave(slave),)
        if test_formations:
            test_formations = {formation.formation_type for formation in test_formations if formation is not None}
            if not self.compatibility.test_collection(test_formations):
                return TestResult(False, '{}, {} are not in compatible formations.', master, slave, tooltip=tooltip)
        return TestResult.TRUE
Example #7
0
class WhiteBlackStateTest(HasTunableSingletonFactory, AutoFactoryInit,
                          event_testing.test_base.BaseTest):
    test_events = ()
    FACTORY_TUNABLES = {
        'participant':
        TunableEnumEntry(
            description=
            '\n            Who or what to apply this test to.\n            ',
            tunable_type=ParticipantType,
            default=ParticipantType.Object),
        'states':
        TunableWhiteBlackList(
            description=
            "\n            The target's states much conform to the white black list.\n            ",
            tunable=TunableReference(
                description=
                '\n                Allowed and disallowed states.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.OBJECT_STATE),
                pack_safe=True))
    }

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

    @cached_test
    def __call__(self, test_targets=tuple()):
        for target in test_targets:
            if target.state_component is None:
                return TestResult(False,
                                  '{} does not have a state component',
                                  target,
                                  tooltip=self.tooltip)
            current_states = list(target.state_component.values())
            if not self.states.test_collection(current_states):
                return TestResult(
                    False,
                    "{}'s current states do not match the WhiteBlackList that has been defined.",
                    target,
                    tooltip=self.tooltip)
        return TestResult.TRUE
Example #8
0
class NotebookCategoriesTest(HasTunableSingletonFactory, AutoFactoryInit,
                             BaseTest):
    FACTORY_TUNABLES = {
        'subject':
        TunableEnumEntry(
            description='\n            The subject of the test.\n            ',
            tunable_type=ParticipantTypeActorTargetSim,
            default=ParticipantTypeActorTargetSim.Actor),
        'unlocked_categories':
        TunableWhiteBlackList(
            description=
            '\n            This white/black list will check whether or not the subject has\n            unlocked notebook categories.\n            ',
            tunable=TunableEnumEntry(
                description=
                '\n                Notebook categories.\n                ',
                tunable_type=NotebookCategories,
                default=NotebookCategories.INVALID,
                invalid_enums=(NotebookCategories.INVALID, ),
                pack_safe=True))
    }

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

    @cached_test
    def __call__(self, subject=None):
        subject = next(iter(subject))
        tracker = subject.notebook_tracker
        if tracker is None:
            return TestResult(False,
                              'Sim {} has no notebook tracker',
                              subject,
                              tooltip=self.tooltip)
        if not self.unlocked_categories.test_collection(
                tracker.unlocked_category_ids):
            return TestResult(
                False,
                'Sim {} do not meet white/black list unlocked notebook categories requirements.',
                subject,
                tooltip=self.tooltip)
        return TestResult.TRUE
Example #9
0
        class _SpecificCourses(HasTunableSingletonFactory, AutoFactoryInit):
            FACTORY_TUNABLES = {'courses': TunableWhiteBlackList(description='\n                    A list of courses to test the course career slot for. A sim must \n                    have at least one course in their whitelist and none in their \n                    blacklist to pass (by default). \n                    ', tunable=TunablePackSafeReference(description='\n                        A specific course to test the course career slot(s) for.\n                        ', manager=services.get_instance_manager(sims4.resources.Types.UNIVERSITY_COURSE_DATA)))}

            def test(self, actor, course_slots, invert, course_completion_status, tooltip=None):
                degree_tracker = actor.sim_info.degree_tracker
                if degree_tracker is None:
                    return TestResult(False, "Actor {} doesn't have degree tracker.", actor, tooltip=tooltip)
                for career_slot in course_slots:
                    if career_slot is None:
                        continue
                    course_info = degree_tracker.course_infos.get(career_slot.guid64)
                    if course_info is None:
                        continue
                    if course_completion_status.test(actor, course_info):
                        continue
                    course_data = course_info.course_data
                    if self.courses.test_item(course_data):
                        if invert:
                            return TestResult(False, "Actor {}'s course {} is associated with at least one of the specified course career slots", actor, course_data, tooltip=tooltip)
                        return TestResult.TRUE
                if invert:
                    return TestResult.TRUE
                return TestResult(False, "Actor {}'s course career slots and valid course datas do not match.", actor, tooltip=tooltip)
Example #10
0
    class CourseGradeTest(HasTunableSingletonFactory, AutoFactoryInit):

        class _KnownGrade(HasTunableSingletonFactory, AutoFactoryInit):

            def test(self, actor, final_grade, known_grade, grades_to_match, tooltip=None):
                if grades_to_match.test_item(known_grade):
                    return TestResult.TRUE
                return TestResult(False, "Actor {}'s course grade does not pass the white/black list.", actor, tooltip=tooltip)

        class _FinalGrade(HasTunableSingletonFactory, AutoFactoryInit):
            FACTORY_TUNABLES = {'use_known_as_fallback': Tunable(description="\n                    If enabled, the test will run against the sim's known grade \n                    if the final grade cannot be determined. \n                    ", tunable_type=bool, default=True)}

            def test(self, actor, final_grade, known_grade, grades_to_match, tooltip=None):
                grade = final_grade
                if grade == Grade.UNKNOWN:
                    if self.use_known_as_fallback:
                        grade = known_grade
                if grades_to_match.test_item(grade):
                    return TestResult.TRUE
                return TestResult(False, "Actor {}'s course grade does not pass the white/black list.", actor, tooltip=tooltip)

        FACTORY_TUNABLES = {'actor': TunableEnumEntry(description='\n                The actor whose course grade we will test. \n                ', tunable_type=ParticipantTypeSingleSim, default=ParticipantTypeSingleSim.Actor), 'course_career_slot': TunablePackSafeReference(description='\n                The course career slot we will test the grade of.\n                ', manager=services.get_instance_manager(sims4.resources.Types.CAREER), class_restrictions='UniversityCourseCareerSlot'), 'grades': TunableWhiteBlackList(description="\n                The white/black list of grades that the sim's course grade must\n                match.\n                ", tunable=TunableEnumEntry(description='\n                    A grade to test against.\n                    ', tunable_type=Grade, default=Grade.UNKNOWN)), 'grade_type': TunableVariant(description='\n                The grade type to compare against.\n                ', known_grade=_KnownGrade.TunableFactory(), final_grade=_FinalGrade.TunableFactory(), default='final_grade')}

        def get_expected_args(self):
            return {'actors': self.actor}

        def test(self, tooltip=None, actors=()):
            actor = next(iter(actors), None)
            if actor is None:
                return TestResult(False, "Actor {} doesn't exist.", self.actor, tooltip=tooltip)
            degree_tracker = actor.sim_info.degree_tracker
            if degree_tracker is None:
                return TestResult(False, "Sim {} doesn't have degree tracker.", actor, tooltip=tooltip)
            if self.course_career_slot is None:
                return TestResult(False, 'The specified course career slot is None, probably because of packsafeness.', tooltip=tooltip)
            course_data = degree_tracker.course_infos.get(self.course_career_slot.guid64)
            if course_data is None:
                return TestResult(False, "Actor {} doesn't have course data associated with career {}.", actor, self.course_career_slot, tooltip=tooltip)
            final_grade = course_data.final_grade
            known_grade = course_data.known_grade
            return self.grade_type.test(actor, final_grade, known_grade, self.grades, tooltip=tooltip)
Example #11
0
class ZoneTest(HasTunableSingletonFactory, AutoFactoryInit, BaseTest):
    test_events = (TestEvent.SimTravel,)
    FACTORY_TUNABLES = {'zone_source': TunableVariant(description='\n            Which zone we want to test.\n            ', use_current_zone=ActiveZone.TunableFactory(), use_pick_info=PickInfoZone.TunableFactory(), use_picked_zone_ids=PickedZoneIds.TunableFactory(), use_participant_home_zone=ParticipantHomeZone.TunableFactory(), default='use_current_zone'), 'zone_tests': TunableTuple(description='\n            The tests we wish to run on the zone in question.\n            ', venue_type=OptionalTunable(description="\n                If checked, will verify the zone's venue type is allowed or\n                disallowed.\n                ", disabled_name="Don't_Test", tunable=TunableWhiteBlackList(description="\n                    The zone's venue type must pass the whitelist and blacklist\n                    to pass the test.\n                    ", tunable=TunableReference(description='\n                        Allowed and disallowed venue types.\n                        ', manager=services.get_instance_manager(sims4.resources.Types.VENUE), pack_safe=True))), use_source_venue=Tunable(description='\n                If enabled, the test will test the source venue instead of the active\n                venue.  For example, the Community Lot instead of the active Marketplace.\n                Testing the active venue is the default.\n                ', tunable_type=bool, default=False), venue_tier=OptionalTunable(description='\n                If checked, will verify that the zone\'s venue is at the tuned \n                tier. If "no valid tier" is selected, this test will be True\n                if either the current venue doesn\'t have tiers or if it does but\n                it doesn\'t currently meet any of their requirements.\n                ', tunable=TunableVariant(description='\n                    ', tier_number=TunableRange(description='\n                        The index of the tier to test. This test will return\n                        true if this tier is active and false otherwise. This\n                        should be the index of the tier in the tier list and not\n                        any player-facing index. For instance, if a tier list\n                        had a single tier, that tier would be 0, and if a second\n                        tier were added, that second tier would be 1.\n                        ', tunable_type=int, minimum=0, default=0), locked_args={'no_valid_tier': -1})), is_apartment=OptionalTunable(description='\n                If checked, test will pass if the zone is an apartment. If\n                unchecked, test passes if the zone is NOT an apartment. Useful\n                 in aspiration tuning, to discriminate between property\n                types in tests of lot value. Allows "Own a House worth X" and\n                "Own an Apartment worth X"\n                ', disabled_name="Don't_Test", enabled_name='Is_or_is_not_apartment_zone', tunable=TunableTuple(description='\n                    Test whether the zone is an apartment or not.\n                    ', is_apartment=Tunable(description='\n                        If checked, test will pass if the zone is an apartment.\n                        If unchecked, test passes if the zone is NOT an\n                        apartment.\n                        ', tunable_type=bool, default=True), consider_penthouse_an_apartment=Tunable(description='\n                        If enabled, we will consider penthouses to be\n                        apartments when testing them against the apartment\n                        check.\n                        ', tunable_type=bool, default=True))), is_penthouse=OptionalTunable(description='\n                If enabled, test whether or not the current zone is a penthouse.\n                ', tunable=Tunable(description='\n                    If checked, the zone must be a penthouse. If unchecked, the\n                    zone cannot be a penthouse.\n                    ', tunable_type=bool, default=True)), zone_modifiers=OptionalTunable(description='\n                if enabled, we test the zone modifiers allowed or disallowed.\n                ', disabled_name="Don't_Test", tunable=TunableWhiteBlackList(description="\n                    The zone's modifiers must pass this whitelist and blacklist for the\n                    test to pass.\n                    ", tunable=TunableReference(description='\n                        Allowed and disallowed zone modifiers.\n                        ', manager=services.get_instance_manager(sims4.resources.Types.ZONE_MODIFIER), pack_safe=True))), world_tests=OptionalTunable(description='\n                If enabled, we test if specified zone is or is not in the specified world(s)\n                ', tunable=TunableWhiteBlackList(description='\n                    Pass if zone is in one of the worlds in the whitelist,\n                    or fail if it is any of the worlds in the blacklist.\n                    ', tunable=TunableWorldDescription(description='\n                        World to check against.\n                        ', pack_safe=True))), business_tests=OptionalTunable(description='\n                If enabled, test if the specified zone is a business or not.\n                ', disabled_name="Don't_Test", tunable=TunableVariant(description='\n                    Test if the zone is a business, an open business, or a\n                    closed business.\n                    ', is_business=_IsBusinessTest.TunableFactory(), is_business_open=_IsBusinessOpenTest.TunableFactory(), default='is_business')))}

    def get_expected_args(self):
        return self.zone_source.get_expected_args()

    def __call__(self, *args, **kwargs):
        zone_id = self.zone_source.get_zone_id(**kwargs)
        if not zone_id:
            return TestResult(False, "ZoneTest couldn't find a zone to test.", tooltip=self.tooltip)
        if self.zone_tests.venue_type is not None:
            venue_service = services.venue_service()
            if self.zone_tests.use_source_venue:
                venue_tuning = type(venue_service.source_venue)
            else:
                venue_tuning = type(venue_service.active_venue)
            venue_tunings = (venue_tuning,) if venue_tuning is not None else ()
            if not self.zone_tests.venue_type.test_collection(venue_tunings):
                return TestResult(False, 'Zone failed venue white or black list {}', venue_tuning, tooltip=self.tooltip)
        if self.zone_tests.venue_tier is not None:
            venue_tier_index = build_buy.get_venue_tier(zone_id)
            if self.zone_tests.venue_tier != venue_tier_index:
                return TestResult(False, 'Zone has tier {} but {} was required', venue_tier_index, self.zone_tests.venue_tier, tooltip=self.tooltip)
        if self.zone_tests.is_apartment is not None:
            plex_service = services.get_plex_service()
            if self.zone_tests.is_apartment.is_apartment != plex_service.is_zone_an_apartment(zone_id, consider_penthouse_an_apartment=self.zone_tests.is_apartment.consider_penthouse_an_apartment):
                return TestResult(False, 'Zone failed apartment test', tooltip=self.tooltip)
        if self.zone_tests.is_penthouse is not None:
            plex_service = services.get_plex_service()
            is_penthouse = plex_service.get_plex_building_type(zone_id) == PlexBuildingType.PENTHOUSE_PLEX
            if is_penthouse != self.zone_tests.is_penthouse:
                return TestResult(False, 'Zone failed penthouse test', tooltip=self.tooltip)
        if self.zone_tests.zone_modifiers is not None:
            zone_modifier_service = services.get_zone_modifier_service()
            zone_modifiers = zone_modifier_service.get_zone_modifiers(zone_id)
            if not self.zone_tests.zone_modifiers.test_collection(zone_modifiers):
                return TestResult(False, 'Zone failed to meet whitelist/blacklist for zone modifiers. ZoneId: {}, Mods: {}', zone_id, zone_modifiers, tooltip=self.tooltip)
        if self.zone_tests.world_tests is not None:
            world_id = services.get_persistence_service().get_world_id_from_zone(zone_id)
            world_desc_id = services.get_world_description_id(world_id)
            if world_desc_id == 0:
                return TestResult(False, 'Unable to determine world for Zone {}', zone_id)
            if not self.zone_tests.world_tests.test_item(world_desc_id):
                return TestResult(False, 'Zone {} failed to meet world requirements, is in {}, fails tests for {}', zone_id, world_desc_id, self.zone_tests.world_tests, tooltip=self.tooltip)
        if self.zone_tests.business_tests is not None:
            return self.zone_tests.business_tests(zone_id)
        return TestResult.TRUE
Example #12
0
class UniversityEnrollmentTest(HasTunableSingletonFactory, AutoFactoryInit, event_testing.test_base.BaseTest):

    class _SpecificMajors(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {'_majors': TunableWhiteBlackList(description="\n                The sim's enrolled major must match against the whitelist and blacklist\n                to pass.\n                ", tunable=TunableReference(description='\n                    A University major to test against.\n                    ', manager=services.get_instance_manager(sims4.resources.Types.UNIVERSITY_MAJOR)))}

        def is_valid_major(self, sim_info, tooltip=None):
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is None:
                return TestResult(False, '{0} has no degree tracker.', sim_info, tooltip=tooltip)
            major = degree_tracker.get_major()
            if not self._majors.test_item(major):
                return TestResult(False, "{0}'s enrolled majors do not match the tuned whitelist/blacklist.", sim_info, tooltip=tooltip)
            return TestResult.TRUE

    class _AnyMajors(HasTunableSingletonFactory, AutoFactoryInit):

        def is_valid_major(self, sim_info, tooltip=None):
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is None:
                return TestResult(False, '{0} has no degree tracker.', sim_info, tooltip=tooltip)
            if degree_tracker.get_enrolled_major() is None:
                return TestResult(False, '{0} is not enrolled in a major.', sim_info, tooltip=tooltip)
            return TestResult.TRUE

    class _NoMajor(HasTunableSingletonFactory, AutoFactoryInit):

        def is_valid_major(self, sim_info, tooltip=None):
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is not None and degree_tracker.get_enrolled_major() is not None:
                return TestResult(False, '{0} is enrolled in a major.', sim_info, tooltip=tooltip)
            return TestResult.TRUE

    FACTORY_TUNABLES = {'major': TunableVariant(description='\n            Which major(s) the sim must be pursuing.\n            ', any_majors=_AnyMajors.TunableFactory(), specific_majors=_SpecificMajors.TunableFactory(), no_major=_NoMajor.TunableFactory(), locked_args={'disabled': None}, default='any_majors'), 'university': OptionalTunable(description='\n            University in which the sim must be enrolled.\n            If Disabled, sim can be in any university.\n            ', tunable=TunableReference(description='\n                The university to filter for.\n                ', manager=services.get_instance_manager(Types.UNIVERSITY))), 'enrollment_status': OptionalTunable(description='\n            Enrollment status to test against. \n            If Disabled, sim can have any enrollment status.\n            ', disabled_name="Don't_Test", tunable=TunableWhiteBlackList(description="\n                The sim's enrollment status must match the whitelist and blacklist\n                to pass.\n                ", tunable=TunableEnumEntry(description='\n                    The enrollment status to check against.\n                    ', tunable_type=EnrollmentStatus, default=EnrollmentStatus.NONE))), 'subject': TunableEnumEntry(description='\n            The subject of this test.\n            ', tunable_type=ParticipantTypeSim, default=ParticipantTypeSim.Actor), 'match_type': TunableEnumEntry(description='\n            When testing multiple participants if MATCH_ALL is set, then all the\n            participants need to pass the test.\n             \n            If MATCH_ANY is set, test will pass as soon as one of them meet the\n            criteria\n            ', tunable_type=MatchType, default=MatchType.MATCH_ALL)}

    def is_valid(self, sim_info, tooltip=None):
        if self.major is not None:
            valid_major_result = self.major.is_valid_major(sim_info, tooltip=tooltip)
            if not valid_major_result:
                return valid_major_result
        degree_tracker = sim_info.degree_tracker
        if degree_tracker is None:
            return TestResult(False, "{0} doesn't have a degree tracker.", sim_info, tooltip=tooltip)
        if self.university is not None:
            current_university = degree_tracker.get_university()
            if current_university is None:
                return TestResult(False, '{0} has no university specified in their degree tracker.', sim_info, tooltip=tooltip)
            if current_university.guid64 != self.university.guid64:
                return TestResult(False, '{0} is not enrolled in the correct university.', sim_info, tooltip=tooltip)
        if self.enrollment_status is not None and not self.enrollment_status.test_item(degree_tracker._enrollment_status):
            return TestResult(False, '{0} does not pass the whitelist/blacklist for university enrollment status', sim_info, tooltip=tooltip)
        return TestResult.TRUE

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

    @cached_test
    def __call__(self, test_targets, targets=None, tooltip=None):
        if not test_targets:
            return TestResult(False, 'UniversityEnrollmentTest failed due no targets.', tooltip=self.tooltip)
        if self.match_type == MatchType.MATCH_ALL:
            for target in test_targets:
                result = self.is_valid(target, tooltip=self.tooltip)
                if not result:
                    return result
            return TestResult.TRUE
        for target in test_targets:
            result = self.is_valid(target, tooltip=self.tooltip)
            if result:
                return result
        return result
Example #13
0
class TraitPickerSuperInteraction(PickerSuperInteraction):
    INSTANCE_TUNABLES = {
        'is_add':
        Tunable(
            description=
            '\n            If this interaction is trying to add a trait to the sim or to\n            remove a trait from the sim.\n            ',
            tunable_type=bool,
            default=True),
        'already_equipped_tooltip':
        OptionalTunable(
            description=
            '\n            If tuned, we show this tooltip if row is disabled when trait is \n            already equipped.\n            ',
            tunable=TunableLocalizedStringFactory(
                description=
                '\n                Tooltip to display.\n                ')),
        'filter_by_types':
        OptionalTunable(
            description=
            '\n            If specified, limits the traits that appear in this picker to specific types of traits.\n            If disabled, all traits are available.\n            ',
            tunable=TunableWhiteBlackList(tunable=TunableEnumEntry(
                default=TraitType.PERSONALITY, tunable_type=TraitType)))
    }

    def _run_interaction_gen(self, timeline):
        self._show_picker_dialog(self.target, target_sim=self.target)
        return True
        yield

    @classmethod
    def _match_trait_type(cls, trait):
        if cls.filter_by_types is None:
            return True
        return cls.filter_by_types.test_item(trait.trait_type)

    @classmethod
    def _trait_selection_gen(cls, target):
        trait_manager = services.get_instance_manager(
            sims4.resources.Types.TRAIT)
        trait_tracker = target.sim_info.trait_tracker
        if cls.is_add:
            for trait in trait_manager.types.values():
                if not cls._match_trait_type(trait):
                    continue
                if trait.sim_info_fixup_actions:
                    continue
                if trait_tracker.can_add_trait(trait):
                    yield trait
        else:
            for trait in trait_tracker.equipped_traits:
                if not cls._match_trait_type(trait):
                    continue
                yield trait

    @flexmethod
    def picker_rows_gen(cls, inst, target, context, **kwargs):
        trait_tracker = target.sim_info.trait_tracker
        for trait in cls._trait_selection_gen(target):
            if trait.display_name:
                display_name = trait.display_name(target)
                is_enabled = True
                row_tooltip = None
                is_enabled = not trait_tracker.has_trait(trait)
                row_tooltip = None if is_enabled or cls.already_equipped_tooltip is None else lambda *_: cls.already_equipped_tooltip(
                    target)
                row = ObjectPickerRow(
                    name=display_name,
                    row_description=trait.trait_description(target),
                    icon=trait.icon,
                    tag=trait,
                    is_enable=is_enabled,
                    row_tooltip=row_tooltip)
                yield row

    def on_choice_selected(self, choice_tag, **kwargs):
        trait = choice_tag
        if trait is not None:
            if self.is_add:
                self.target.sim_info.add_trait(trait)
            else:
                self.target.sim_info.remove_trait(trait)
Example #14
0
class FamiliarTest(HasTunableSingletonFactory, AutoFactoryInit, BaseTest):
    FACTORY_TUNABLES = {'subject': TunableEnumEntry(description="\n            The subject who's familiar tracker we are checking.\n            ", tunable_type=ParticipantTypeSingle, default=ParticipantTypeSingle.Actor), 'familiar_type_filter': OptionalTunable(description='\n            If enabled then we will filter based on the familiar type.\n            ', tunable=TunableWhiteBlackList(description='\n                A filter on the type of familiar that is active.\n                ', tunable=TunableEnumEntry(description='\n                    The type of familiar to check for.\n                    ', tunable_type=FamiliarType, default=FamiliarType.CAT))), 'negate': Tunable(description='\n            If checked then we will negate the results of this test.\n            ', tunable_type=bool, default=False)}

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

    def __call__(self, subject=None):
        for sim in subject:
            familiar_tracker = sim.sim_info.familiar_tracker
            if familiar_tracker is None:
                if self.negate:
                    return TestResult.TRUE
                return TestResult(False, '{} does not have a familiar tracker.', sim, tooltip=self.tooltip)
            active_familiar = familiar_tracker.get_active_familiar()
            if active_familiar is None or active_familiar.is_hidden():
                if self.negate:
                    return TestResult.TRUE
                return TestResult(False, '{} does not have an active familiar.', sim, tooltip=self.tooltip)
            if self.familiar_type_filter is not None:
                if not self.familiar_type_filter.test_item(familiar_tracker.active_familiar_type):
                    if self.negate:
                        return TestResult.TRUE
                    return TestResult(False, "{}'s familiar is of type {} which doesn't pass the filter.", sim, familiar_tracker.active_familiar_type, tooltip=self.tooltip)
        if self.negate:
            return TestResult(False, 'All sims pass the familiar requirements.', tooltip=self.tooltip)
        return TestResult.TRUE
Example #15
0
    class BaseAppearanceModification(HasTunableSingletonFactory,
                                     AutoFactoryInit):
        FACTORY_TUNABLES = {
            '_is_combinable_with_same_type':
            Tunable(
                description=
                '\n                True if this modifier type is able to be combined with another\n                of its type. If True, and two modifiers conflict, then the tuned\n                priority will be used to resolve the conflict. If False, only\n                a single modifier of this type with the highest priority will be shown.\n                ',
                tunable_type=bool,
                default=True),
            'outfit_type_compatibility':
            OptionalTunable(
                description=
                '\n                If enabled, will verify when switching outfits if the new\n                outfit is compatible with this appearance modifier.\n                ',
                disabled_name="Don't_Test",
                tunable=TunableWhiteBlackList(
                    description=
                    '\n                    The outfit category must match the whitelist and blacklist\n                    to be applied.\n                    ',
                    tunable=TunableEnumEntry(
                        description=
                        '\n                        The outfit category want to test against the \n                        apperance modifier.\n                        ',
                        tunable_type=OutfitCategory,
                        default=OutfitCategory.EVERYDAY))),
            'appearance_modifier_tag':
            OptionalTunable(
                description=
                '\n                If enabled, a tag used to reference this appearance modifier.\n                ',
                tunable=TunableTag(
                    description=
                    '\n                    Tag associated with this appearance modifier.\n                    '
                ))
        }

        def modify_sim_info(self, source_sim_info, modified_sim_info,
                            random_seed):
            raise NotImplementedError(
                'Attempting to use the BaseAppearanceModification base class, use sub-classes instead.'
            )

        @property
        def is_permanent_modification(self):
            return False

        @property
        def modifier_type(self):
            raise NotImplementedError(
                'Attempting to use the BaseAppearanceModification base class, use sub-classes instead.'
            )

        @property
        def is_combinable_with_same_type(self):
            return self._is_combinable_with_same_type

        @property
        def combinable_sorting_key(self):
            raise NotImplementedError(
                'Attempting to use the BaseAppearanceModification base class, use sub-classes instead.'
            )

        def is_compatible_with_outfit(self, outfit_category):
            if self.outfit_type_compatibility is None:
                return True
            return self.outfit_type_compatibility.test_item(outfit_category)
    class _SpecificScholarships(HasTunableSingletonFactory, AutoFactoryInit):
        FACTORY_TUNABLES = {
            '_scholarships':
            TunableWhiteBlackList(
                description=
                '\n                Scholarships against which to test against application status.\n                ',
                tunable=TunablePackSafeReference(
                    description=
                    '\n                    The scholarship instance to check.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.SNIPPET))),
            '_status':
            TunableEnumEntry(
                description=
                '\n                Status of the scholarship(s).\n                ',
                tunable_type=ScholarshipStatus,
                default=ScholarshipStatus.ACCEPTED)
        }

        def is_valid_scholarship(self, sim_info, tooltip=None):
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is None:
                return TestResult(False,
                                  '{} has no degree tracker.',
                                  sim_info,
                                  tooltip=tooltip)

            def _scholarship_container_helper(scholarships_of_status, sim_info,
                                              tooltip):
                snippet_manager = services.get_instance_manager(
                    sims4.resources.Types.SNIPPET)
                scholarship_insts_of_status = [
                    snippet_manager.get(scholarship)
                    for scholarship in scholarships_of_status
                ]
                if not self._scholarships.test_collection(
                        scholarship_insts_of_status):
                    return TestResult(
                        False,
                        "{0}'s scholarships do not match the tuned whitelist/blacklist.",
                        sim_info,
                        tooltip=tooltip)
                return TestResult.TRUE

            if self._status == ScholarshipStatus.ACTIVE:
                return _scholarship_container_helper(
                    degree_tracker.get_active_scholarships(), sim_info,
                    tooltip)
            if self._status == ScholarshipStatus.REJECTED:
                return _scholarship_container_helper(
                    degree_tracker.get_rejected_scholarships(), sim_info,
                    tooltip)
            if self._status == ScholarshipStatus.ACCEPTED:
                return _scholarship_container_helper(
                    degree_tracker.get_accepted_scholarships(), sim_info,
                    tooltip)
            if self._status == ScholarshipStatus.PENDING:
                return _scholarship_container_helper(
                    degree_tracker.get_pending_scholarships(), sim_info,
                    tooltip)
            return TestResult.TRUE
Example #17
0
class RoutingFormation(HasTunableReference,
                       metaclass=HashedTunedInstanceMetaclass,
                       manager=services.snippet_manager()):
    INSTANCE_TUNABLES = {
        'formation_behavior':
        RoutingFormationBehavior.TunableFactory(),
        'formation_routing_type':
        TunableVariant(
            description=
            '\n            The purpose of the routing formation which governs how the slave\n            behaves on routes.\n            ',
            follow=FormationTypeFollow.TunableFactory(),
            paired=FormationTypePaired.TunableFactory(),
            default='follow'),
        'formation_compatibility':
        TunableWhiteBlackList(
            description=
            '\n            This routing formation is able to coexist with any other formation\n            listed here. For example, "Walk Dog" on the right side of a Sim is\n            compatible with "Walk Dog" on their left side (and vice-versa).\n            ',
            tunable=TunableReference(manager=services.get_instance_manager(
                sims4.resources.Types.SNIPPET),
                                     class_restrictions=('RoutingFormation', ),
                                     pack_safe=True)),
        'formation_tests':
        TunableTestSet(
            description=
            '\n            A test set to determine whether or not the master and slave can be\n            in a formation together.\n            \n            Master: Participant Actor\n            Slave: Participant Slave\n            '
        ),
        'walkstyle_mapping':
        TunableMapping(
            description=
            '\n            Mapping of Master walkstyles to Slave walkstyles. This is how we\n            ensure that slaves use a walkstyle to keep pace with their masters.\n            \n            Note you do not need to worry about combo replacement walkstyles\n            like GhostRun or GhostWalk. We get the first non-combo from the\n            master and apply the walkstyle to get any combos from the slave.\n            ',
            key_type=TunableWalkstyle(
                description=
                '\n                The walkstyle that the master must be in to apply the value\n                walkstyle to the slave.\n                '
            ),
            value_type=WalkStyleRequest.TunableFactory(),
            key_name='Master Walkstyle',
            value_name='Slave Walkstyle Request'),
        'should_increase_master_agent_radius':
        Tunable(
            description=
            "\n            If enabled, we combine the slave's agent radius with the master's.\n            ",
            tunable_type=bool,
            default=True),
        'allow_slave_to_teleport_with_master':
        Tunable(
            description=
            '\n            If enabled, when the master teleports using a teleport style, the \n            slave will also be teleported nearby.  If this is false, the master\n            cannot use teleport styles at all while they have a routing slave\n            using this data.\n            ',
            tunable_type=bool,
            default=False)
    }

    def __init__(self, master, slave, *args, interaction=None, **kwargs):
        super().__init__(*args, **kwargs)
        self._master = master
        self._slave = slave
        self._interaction = interaction
        self._routing_type = self.formation_routing_type(
            self._master, self._slave, self.formation_type)
        self._formation_behavior = self.formation_behavior(master, slave)
        master.routing_component.add_routing_slave(self)
        if interaction is not None:
            formation_liability = RoutingFormationLiability(self)
            interaction.add_liability(formation_liability.LIABILITY_TOKEN,
                                      formation_liability)
        else:
            logger.callstack(
                'Routing Formation created without an interaction, this should not happen. Slave: {} Master: {} Formation: {}',
                slave, master, self)
            self.release_formation_data()

    @classmethod
    def test_formation(cls, master, slave):
        resolver = DoubleObjectResolver(master, slave)
        return cls.formation_tests.run_tests(resolver)

    @classproperty
    def formation_type(cls):
        return cls

    @property
    def master(self):
        return self._master

    @property
    def slave(self):
        return self._slave

    @classproperty
    def max_slave_count(cls):
        return cls.formation_routing_type.factory.get_max_slave_count(
            cls.formation_routing_type)

    @property
    def offset(self):
        return self._routing_type.offset

    @property
    def route_length_minimum(self):
        return self._routing_type.route_length_minimum

    def on_add(self):
        self.master.register_routing_stage_event(RoutingStageEvent.ROUTE_START,
                                                 self._on_master_route_start)
        self.master.register_routing_stage_event(RoutingStageEvent.ROUTE_END,
                                                 self._on_master_route_end)
        self._formation_behavior.on_add()

    def on_release(self):
        self._routing_type.on_release()
        self._formation_behavior.on_release()
        self.master.unregister_routing_stage_event(
            RoutingStageEvent.ROUTE_START, self._on_master_route_start)
        self.master.unregister_routing_stage_event(RoutingStageEvent.ROUTE_END,
                                                   self._on_master_route_end)

    def attachment_info_gen(self):
        yield from self._routing_type.attachment_info_gen()

    def _on_master_route_start(self, *_, **__):
        self._routing_type.on_master_route_start()

    def _on_master_route_end(self, *_, **__):
        self._routing_type.on_master_route_end()

    def get_routing_slave_constraint(self):
        return self._routing_type.get_routing_slave_constraint()

    def get_walkstyle_override(self):
        walkstyle_request = self.walkstyle_mapping.get(
            self.master.get_walkstyle())
        slaved_walkstyle = self._slave.get_walkstyle()
        if walkstyle_request is not None:
            with self._slave.routing_component.temporary_walkstyle_request(
                    walkstyle_request):
                slaved_walkstyle = self._slave.get_walkstyle()
        return slaved_walkstyle

    def find_good_location_for_slave(self, master_location):
        return self._routing_type.find_good_location_for_slave(master_location)

    def add_routing_slave_to_pb(self, route_pb, path=None):
        slave_pb = route_pb.slaves.add()
        slave_pb.id = self._slave.id
        slave_pb.type = self._routing_type.slave_attachment_type
        walkstyle_override_msg = slave_pb.walkstyle_overrides.add()
        walkstyle_override_msg.from_walkstyle = 0
        walkstyle_override_msg.to_walkstyle = self.get_walkstyle_override()
        for (from_walkstyle,
             to_walkstyle_request) in self.walkstyle_mapping.items():
            walkstyle_override_msg = slave_pb.walkstyle_overrides.add()
            walkstyle_override_msg.from_walkstyle = from_walkstyle
            with self._slave.routing_component.temporary_walkstyle_request(
                    to_walkstyle_request):
                walkstyle_override_msg.to_walkstyle = self._slave.get_walkstyle(
                )
        for attachment_node in self.attachment_info_gen():
            with ProtocolBufferRollback(slave_pb.offset) as attachment_pb:
                attachment_node.populate_attachment_pb(attachment_pb)
        self._routing_type.build_routing_slave_pb(slave_pb, path=path)
        return (self._slave, slave_pb)

    def release_formation_data(self):
        self._routing_type.on_release()
        self._master.routing_component.clear_slave(self._slave)

    def should_slave_for_path(self, path):
        return self._routing_type.should_slave_for_path(path)

    def update_slave_position(self,
                              master_transform,
                              master_orientation,
                              routing_surface,
                              distribute=True,
                              path=None,
                              canceled=False):
        self._routing_type.update_slave_position(master_transform,
                                                 master_orientation,
                                                 routing_surface,
                                                 distribute=distribute,
                                                 path=path,
                                                 canceled=canceled)
Example #18
0
class InRoutingFormation(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {'required_or_prohibited': Tunable(description='\n            If enabled, we require the master/slave to be in a\n            formation of any type. If disabled, we require that the\n            master/slave is not in a formation.\n            ', tunable_type=bool, default=True), 'formations_to_validate': OptionalTunable(description='\n            If enabled depending on the required_or_prohibited check, we will\n            validate if the Sim current formation against the ones on this\n            list.\n            ', tunable=TunableWhiteBlackList(description='\n                A white/blacklist that determines compatibility via\n                required or prohibited formations.\n                ', tunable=TunableReference(description='\n                    A routing formation\n                    ', manager=services.get_instance_manager(sims4.resources.Types.SNIPPET), class_restrictions=('RoutingFormation',), pack_safe=True)))}

    def test(self, master, slave, tooltip):
        if slave is not None:
            if self.required_or_prohibited and slave.routing_component.routing_master is None:
                return TestResult(False, '{} is expected to be the slave in a routing formation.', slave, tooltip=tooltip)
            if not self.required_or_prohibited and slave.routing_component.routing_master is not None:
                return TestResult(False, '{} is not expected to be the slave in a routing formation.', slave, tooltip=tooltip)
        if master is not None:
            slave_data = [formation for formation in master.get_routing_slave_data() if self.formations_to_validate is None or self.formations_to_validate.test_item(formation.formation_type)]
            if self.required_or_prohibited:
                if not slave_data:
                    return TestResult(False, '{} is expected to be the master of a routing formation.', master, tooltip=tooltip)
                if slave is not None:
                    slave_formation = master.get_formation_data_for_slave(slave)
                    if slave_formation is None or not not (self.formations_to_validate is not None and self.formations_to_validate.test_item(slave_formation.formation_type)):
                        return TestResult(False, '{} is expected to be the master of a routing formation with {}', master, slave, tooltip=tooltip)
            elif slave_data:
                return TestResult(False, '{} is not expected to be the master of a routing formation', master, tooltip=tooltip)
        return TestResult.TRUE
class BarSpecialNightSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'end_time': TunableTimeOfDay(description='\n            The time that this situation will end.\n            '), 'special_night_patron': TunableSituationJobAndRoleState(description='\n            The job and role of the special night patron.\n            '), 'notification': TunableUiDialogNotificationSnippet(description='\n            The notification to display when this object reward is granted\n            to the Sim. There is one additional token provided: a string\n            representing a bulleted list of all individual rewards granted.\n            '), 'starting_entitlement': OptionalTunable(description='\n            If enabled, this situation is locked by an entitlement. Otherwise,\n            this situation is available to all players.\n            ', tunable=TunableEnumEntry(description='\n                Pack required for this event to start.\n                ', tunable_type=Pack, default=Pack.BASE_GAME)), 'valid_regions': TunableWhiteBlackList(description='\n            A white/black list of regions in which this schedule entry is valid.\n            For instance, some bar nights might not be valid in the Jungle bar.\n            ', tunable=Region.TunableReference(pack_safe=True))}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _BarSpecialNightSituationState),)

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

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def situation_meets_starting_requirements(cls, **kwargs):
        if not cls.valid_regions.test_item(services.current_region()):
            return False
        if cls.starting_entitlement is None:
            return True
        return is_available_pack(cls.starting_entitlement)

    def _get_duration(self):
        time_now = services.time_service().sim_now
        return time_now.time_till_next_day_time(self.end_time).in_minutes()

    def start_situation(self):
        super().start_situation()
        self._change_state(_BarSpecialNightSituationState())
        dialog = self.notification(services.active_sim_info())
        dialog.show_dialog()

    def _issue_requests(self):
        request = BouncerRequestFactory(self, callback_data=_RequestUserData(role_state_type=self.special_night_patron.role_state), job_type=self.special_night_patron.job, request_priority=BouncerRequestPriority.BACKGROUND_LOW, user_facing=self.is_user_facing, exclusivity=self.exclusivity)
        self.manager.bouncer.submit_request(request)
Example #20
0
class ObjectPart(HasTunableReference,
                 metaclass=TunedInstanceMetaclass,
                 manager=services.object_part_manager()):
    INSTANCE_TUNABLES = {
        'supported_posture_types':
        TunablePostureTypeListSnippet(
            description=
            '\n            The postures supported by this part. If empty, assumes all postures\n            are supported.\n            '
        ),
        'supported_affordance_data':
        TunableTuple(
            description=
            '\n            Define affordance compatibility for this part.\n            ',
            compatibility=TunableAffordanceFilterSnippet(
                description=
                '\n                Affordances supported by the part\n                '
            ),
            consider_mixers=Tunable(
                description=
                '\n                If checked, mixers are filtered through this compatibility\n                check. If unchecked, all mixers are assumed to be valid to run\n                on this part.\n                ',
                tunable_type=bool,
                default=False)),
        'blacklisted_buffs':
        TunableList(
            description=
            '\n            A list of buffs that will disable this part as a candidate to run an\n            interaction.\n            ',
            tunable=TunableReference(
                description=
                '\n               Reference to a buff to disable the part.\n               ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.BUFF),
                pack_safe=True)),
        'trait_requirements':
        TunableWhiteBlackList(
            description=
            '\n            Trait blacklist and whitelist requirements to pick this part.\n            ',
            tunable=Trait.TunableReference(
                description=
                '\n               Reference to the trait white/blacklists.\n               ',
                pack_safe=True)),
        'subroot':
        TunableReference(
            description=
            '\n            The reference of the subroot definition in the part.\n            ',
            manager=services.subroot_manager(),
            allow_none=True),
        'portal_data':
        TunableSet(
            description=
            '\n            If the object owning this part has a portal component tuned, the\n            specified portals will be created for each part of this type. The\n            root position of the part is the subroot position.\n            ',
            tunable=TunablePortalReference(pack_safe=True)),
        'can_pick':
        Tunable(
            description=
            '\n            If checked, this part can be picked (selected as target when\n            clicking on object.)  If unchecked, cannot be picked.\n            ',
            tunable_type=bool,
            default=True),
        'part_surface':
        TunableVariant(
            description=
            '\n            The rules to determine the surface type for this object.\n            ',
            part_owner=_PartOwnerSurfaceType.TunableFactory(),
            override_surface=_OverrideSurfaceType.TunableFactory(),
            default='part_owner')
    }
    _bone_name_hashes_for_part_suffices = None

    @classmethod
    def register_tuned_animation(cls, *_, **__):
        pass

    @classmethod
    def add_auto_constraint(cls, participant_type, tuned_constraint, **kwargs):
        pass

    @classmethod
    def get_bone_name_hashes_for_part_suffix(cls, part_suffix):
        if cls._bone_name_hashes_for_part_suffices is None:
            cls._bone_name_hashes_for_part_suffices = {}
        if part_suffix in cls._bone_name_hashes_for_part_suffices:
            return cls._bone_name_hashes_for_part_suffices[part_suffix]
        bone_name_hashes = set()
        if cls.subroot is not None:
            for bone_name_hash in cls.subroot.bone_names:
                if part_suffix is not None:
                    bone_name_hash = hash32(str(part_suffix),
                                            initial_hash=bone_name_hash)
                bone_name_hashes.add(bone_name_hash)
        cls._bone_name_hashes_for_part_suffices[part_suffix] = frozenset(
            bone_name_hashes)
        return cls._bone_name_hashes_for_part_suffices[part_suffix]
Example #21
0
class SicknessTest(HasTunableSingletonFactory, AutoFactoryInit, BaseTest):
    FACTORY_TUNABLES = {
        'target':
        TunableEnumEntry(
            description=
            '\n            When this test runs, it checks against this participant\n            for sickness information.  If the affordance is targeting \n            a patient, it will typically be TargetSim.\n            ',
            tunable_type=ParticipantTypeSingleSim,
            default=ParticipantTypeSim.TargetSim),
        'sickness':
        TunableVariant(
            description=
            '\n            Optionally specify sickness to test against.\n            \n            If disabled, will check if the sick is sick with any sickness. \n            ',
            locked_args={'any_sickness': None},
            white_blacklist=TunableWhiteBlackList(tunable=TunableReference(
                manager=services.get_instance_manager(Types.SICKNESS),
                class_restrictions=('Sickness', ),
                pack_safe=True)),
            by_tag=_SicknessTagTest.TunableFactory(),
            default='any_sickness'),
        'check_history':
        Tunable(
            description=
            '\n            Whether or not to check sickness history.\n            \n            If False, we only check if they are currently sick\n            with the specified sickness.\n            ',
            tunable_type=bool,
            default=False),
        'invert':
        Tunable(
            description=
            '\n            Whether or not to invert the results of this test.\n            ',
            tunable_type=bool,
            default=False)
    }

    def get_expected_args(self):
        return {'targets': self.target}

    @cached_test
    def __call__(self, targets):
        target_sim = next(iter(targets), None)
        if target_sim is None:
            return TestResult(False, 'Target is None.', tooltip=self.tooltip)
        test_value = False
        if self.check_history:
            test_value = len(
                target_sim.sickness_tracker.previous_sicknesses
            ) > 0 if self.sickness is None else bool(
                self.sickness.test_collection(
                    target_sim.sickness_tracker.previous_sicknesses))
            result_value = self.invert != test_value
            if result_value:
                return TestResult.TRUE
            return TestResult(
                False,
                'Failed previous sickness test. target={}, previous_sicknesses={}, {}'
                .format(target_sim,
                        target_sim.sickness_tracker.previous_sicknesses, self))
        else:
            test_value = target_sim.is_sick(
            ) if self.sickness is None else bool(
                self.sickness.test_item(target_sim.current_sickness))
            result_value = self.invert != test_value
            if result_value:
                return TestResult.TRUE
            else:
                return TestResult(
                    False,
                    'Failed sickness test. target={}, current_sickness={}, {}'.
                    format(target_sim, target_sim.current_sickness, self))
Example #22
0
class UtilitiesComponentTest(HasTunableSingletonFactory, AutoFactoryInit, BaseTest):
    FACTORY_TUNABLES = {'subject': TunableEnumEntry(description='\n            The subject of the test.\n            ', tunable_type=ParticipantTypeObject, default=ParticipantTypeObject.Object), 'can_run_on_utilities': OptionalTunable(description='\n            If enabled, will verify if subject can be run\n            on tuned utilities.\n            ', tunable=TunableWhiteBlackList(description='\n                A White/black list to check whether or not the \n                subject can be run on tuned utilities.\n                ', tunable=TunableEnumEntry(description='\n                    The utility that we want to test.\n                    ', tunable_type=Utilities, default=None)), disabled_name='ignore'), 'is_allowed_utility_usage': OptionalTunable(description='\n            If enabled, will verify if utility is allowed to be used\n            by the subject.\n            ', tunable=TunableMapping(description='\n                A mapping of utility to utility usage.\n                ', key_name='utility', key_type=TunableEnumEntry(description='\n                    The utility that we want to test.\n                    ', tunable_type=Utilities, default=None), value_name='allow_usage', value_type=Tunable(description='\n                    Whether the tuned utility is allowed to be\n                    used by the subject.\n                    ', tunable_type=bool, default=True)), disabled_name='ignore')}

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

    @cached_test
    def __call__(self, test_targets=()):
        for target in test_targets:
            utilities_component = target.get_component(UTILITIES_COMPONENT)
            if utilities_component is None:
                return TestResult(False, "{} doesn't have Utilities Component.", target, tooltip=self.tooltip)
            if self.can_run_on_utilities and not self.can_run_on_utilities.test_collection(list(utilities_component.allow_utility_usage_dict.keys())):
                return TestResult(False, '{} failed the Can-Run-On-Utilities test.', target, tooltip=self.tooltip)
            if self.is_allowed_utility_usage:
                for (utility, allow_usage) in self.is_allowed_utility_usage.items():
                    if allow_usage != utilities_component.is_allowed_utility_usage(utility):
                        return TestResult(False, '{} failed the Is-Allowed-Utility-Usage test.', target, tooltip=self.tooltip)
        return TestResult.TRUE
Example #23
0
class OutfitBodyTypeTest(HasTunableSingletonFactory, AutoFactoryInit,
                         BaseTest):
    FACTORY_TUNABLES = {
        'subject':
        TunableEnumEntry(
            description=
            '\n            The Sim we want to test the body type outfit for.\n            ',
            tunable_type=ParticipantType,
            default=ParticipantType.Actor),
        'outfit_override':
        OptionalTunable(
            description=
            "\n            If enabled, specify a particular outfit to check the body types of.\n            Otherwise we check the subject's current outfit.\n            ",
            tunable=TunableTuple(
                description=
                '\n                The outfit we want to check the body types of.\n                ',
                outfit_category=TunableEnumEntry(
                    description=
                    '\n                    The outfit category.\n                    ',
                    tunable_type=OutfitCategory,
                    default=OutfitCategory.EVERYDAY),
                index=Tunable(
                    description=
                    '\n                    The outfit index.\n                    ',
                    tunable_type=int,
                    default=0))),
        'body_types':
        TunableWhiteBlackList(
            description=
            '\n            The allowed and disallowed body types required to pass this test.\n            All CAS parts of the subject will be used to determine success or\n            failure.\n            ',
            tunable=TunableEnumEntry(
                description=
                '\n                The body type we want the CAS part to support or not support.\n                ',
                tunable_type=BodyType,
                default=BodyType.FULL_BODY,
                invalid_enums=BodyType.NONE))
    }

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

    @cached_test
    def __call__(self, subjects, *args, **kwargs):
        for subject in subjects:
            if subject is None or not subject.is_sim:
                return TestResult(False,
                                  'OutfitBodyTypeTest cannot test {}.',
                                  subject,
                                  tooltip=self.tooltip)
            outfit_category_and_index = subject.get_current_outfit(
            ) if self.outfit_override is None else (
                self.outfit_override.outfit_category,
                self.outfit_override.index)
            if not subject.has_outfit(outfit_category_and_index):
                return TestResult(
                    False,
                    'OutfitBodyTypeTest cannot test {} since they do not have the requested outfit {}.',
                    subject,
                    outfit_category_and_index,
                    tooltip=self.tooltip)
            outfit = subject.get_outfit(*outfit_category_and_index)
            if not self.body_types.test_collection(outfit.body_types):
                return TestResult(
                    False,
                    'OutfitBodyTypeTest subject {} failed list of body types for outfit {}.',
                    subject,
                    outfit_category_and_index,
                    tooltip=self.tooltip)
        return TestResult.TRUE
Example #24
0
class TunableSimTemplate(HasTunableReference,
                         metaclass=TunedInstanceMetaclass,
                         manager=services.get_instance_manager(
                             sims4.resources.Types.SIM_TEMPLATE)):
    INSTANCE_TUNABLES = {
        '_sim_creation_info':
        TunableSimCreator(
            description=
            '\n            The sim creation info that is passed into CAS in order to create the\n            sim.\n            '
        ),
        '_skills':
        TunableTuple(
            description=
            '\n            Skill that will be added to created sim.\n            ',
            explicit=TunableList(
                description=
                '\n                Skill that will be added to sim\n                ',
                tunable=TunableTuple(
                    skill=statistics.skill.Skill.TunableReference(
                        description=
                        '\n                        The skill that will be added.\n                        ',
                        pack_safe=True),
                    range=SkillRange.TunableFactory(
                        description=
                        '\n                        The possible skill range for a skill that will be added\n                        to the generated sim.\n                        '
                    ))),
            random=OptionalTunable(
                description=
                '\n                Enable if you want random amount of skills to be added to sim.\n                ',
                tunable=TunableTuple(
                    interval=TunableInterval(
                        description=
                        '\n                        Additional random number skills to be added from the\n                        random list.\n                        ',
                        tunable_type=int,
                        default_lower=1,
                        default_upper=1,
                        minimum=0),
                    choices=TunableList(
                        description=
                        '\n                        A list of skills that will be chose for random update.\n                        ',
                        tunable=TunableTuple(
                            skill=statistics.skill.Skill.TunableReference(
                                description=
                                '\n                                The skill that will be added. If left blank a\n                                random skill will be chosen that is not in the\n                                blacklist.\n                                ',
                                pack_safe=True),
                            range=SkillRange.TunableFactory(
                                description=
                                '\n                                The possible skill range for a skill that will\n                                be added to the generated sim.\n                                '
                            )))),
                disabled_name='no_extra_random',
                enabled_name='additional_random'),
            blacklist=TunableSet(
                description=
                '\n                A list of skills that that will not be chosen if looking to set\n                a random skill.\n                ',
                tunable=statistics.skill.Skill.TunableReference())),
        '_traits':
        TunableTuple(
            description=
            '\n            Traits that will be added to the generated template.\n            ',
            explicit=TunableList(
                description=
                '\n                A trait that will always be added to sim.\n                ',
                tunable=TunableReference(manager=services.get_instance_manager(
                    sims4.resources.Types.TRAIT))),
            num_random=OptionalTunable(
                description=
                '\n                If enabled a random number of personality traits that will be\n                added to generated sim.\n                ',
                tunable=TunableInterval(tunable_type=int,
                                        default_lower=1,
                                        default_upper=1,
                                        minimum=0)),
            blacklist=TunableSet(
                description=
                '\n                A list of traits that will not be considered when giving random\n                skills.\n                ',
                tunable=TunableReference(manager=services.get_instance_manager(
                    sims4.resources.Types.TRAIT)))),
        '_ranks':
        TunableList(
            description=
            '\n            The ranked statistics that we want to set on the Sim.\n            ',
            tunable=TunableTuple(
                ranked_statistic=TunablePackSafeReference(
                    description=
                    '\n                    The ranked statistic that we are going to set.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.
                        STATISTIC),
                    class_restrictions=('RankedStatistic', )),
                rank=Tunable(
                    description=
                    '\n                    The rank value for this filter.\n                    ',
                    tunable_type=int,
                    default=1))),
        '_perks':
        TunableTuple(
            description=
            '\n            Perks that will be added to the generated template.\n            ',
            explicit=TunableList(
                description=
                '\n                A perk that will always be added to sim.\n                ',
                tunable=TunableReference(manager=services.get_instance_manager(
                    sims4.resources.Types.BUCKS_PERK))),
            num_random=OptionalTunable(
                description=
                '\n                If enabled, we want random amount of perks to be added to sim.\n                ',
                tunable=TunableInterval(tunable_type=int,
                                        default_lower=1,
                                        default_upper=1,
                                        minimum=0)),
            whiteblacklist=TunableWhiteBlackList(
                description=
                '\n                Pass if perk is in one of the perks in the whitelist, or \n                fail if it is any of the perks in the blacklist.\n                ',
                tunable=TunableReference(manager=services.get_instance_manager(
                    sims4.resources.Types.BUCKS_PERK),
                                         pack_safe=True))),
        '_major':
        OptionalTunable(
            description=
            '\n            When enabled allows you to specify a major and university to enroll\n            the Sim into.\n            ',
            tunable=TunableTuple(
                description=
                '\n                The degree that will be added to the generated Sim.\n                ',
                university=OptionalTunable(
                    description=
                    '\n                    When enabled allows you to specify which university the Sim \n                    should be attending. When not enabled a random university will\n                    be assigned.\n                    ',
                    tunable=TunableReference(
                        description=
                        '\n                        The university to assign the Sim to when enrolling in a \n                        degree\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.UNIVERSITY),
                        pack_safe=True),
                    disabled_name='Random',
                    enabled_name='Specific'),
                major=OptionalTunable(
                    description=
                    '\n                    When enabled allows you to specify which major the Sim will be\n                    enrolled in. When not enabled a random major will be chosen.\n                    ',
                    tunable=TunableReference(
                        description=
                        '\n                        The degree to enroll the Sim into.\n                        ',
                        manager=services.get_instance_manager(
                            sims4.resources.Types.UNIVERSITY_MAJOR)),
                    disabled_name='Random',
                    enabled_name='Specific'),
                num_courses_to_enroll_in=TunableRange(
                    description=
                    '\n                    The amount of courses to enroll in for the chosen major. \n                    ',
                    tunable_type=int,
                    minimum=1,
                    default=1),
                random_credits_in_range=TunableInterval(
                    description=
                    '\n                    The range of random values to be added to the credit count.\n                    ',
                    tunable_type=int,
                    default_upper=1,
                    default_lower=0,
                    minimum=0,
                    maximum=12))),
        '_fixups':
        TunableList(
            description=
            '\n            Sim info fixups that will be added to the generated sim.\n            ',
            tunable=TunablePackSafeReference(
                manager=services.get_instance_manager(
                    sims4.resources.Types.SIM_INFO_FIXUP)))
    }

    @classmethod
    def _verify_tuning_callback(cls):
        for trait in cls._traits.explicit:
            if trait is not None:
                if trait in cls._traits.blacklist:
                    logger.error(
                        'SimTemplate: {} - explicit trait ({}) in blacklist.Either update explicit list or remove from blacklist',
                        cls.__name__,
                        trait.__name__,
                        owner='designer')
        for perk in cls._perks.explicit:
            if perk is not None:
                if not cls._perks.whiteblacklist.test_item(perk):
                    logger.error(
                        'SimTemplate: {} - explicit perk ({}) failed to meetwhitelist/blacklist requirements.Either update explicit list or whitelist/blacklist',
                        cls.__name__,
                        perk.__name__,
                        owner='designer')
        for skill_data in cls._skills.explicit:
            if skill_data.skill is not None:
                if skill_data.skill in cls._skills.blacklist:
                    logger.error(
                        'SimTemplate: {} - in explicit skill ({}) in blacklist.Either update explicit list or remove from blacklist',
                        cls.__name__,
                        skill_data.skill.__name__,
                        owner='designer')
        if cls._skills.random:
            random_skill_available = any(
                skill_data.skill is None
                for skill_data in cls._skills.random.choices)
            if not random_skill_available and len(
                    cls._skills.random.choices
            ) < cls._skills.random.interval.upper_bound:
                logger.error(
                    'SimTemplate: {} - There is not enough entries {} in the random choices to support the upper bound {} of the random amount to add.\n  Possible Fixes:\n    Add a random option into the random->choices \n    Add more options in random->choices\n    or decrease upper bound of random amount.',
                    cls.__name__,
                    len(cls._skills.random.choices),
                    cls._skills.random.interval.upper_bound,
                    owner='designer')
            for skill_data in cls._skills.random.choices:
                if skill_data.skill is not None:
                    if skill_data.skill in cls._skills.blacklist:
                        logger.error(
                            'SimTemplate: {} - in random choices skill {} in blacklist.Either update explicit list or remove from blacklist',
                            cls.__name__,
                            skill_data.skill,
                            owner='designer')

    @classproperty
    def template_type(cls):
        return SimTemplateType.SIM

    @classproperty
    def sim_creator(cls):
        return cls._sim_creation_info()

    @classmethod
    def _get_sim_info_resource_data(cls, resource_key):
        sim_info = SimInfoBaseWrapper()
        sim_info.load_from_resource(resource_key)
        return {
            'age_range': (sim_info.age, sim_info.age),
            'gender': sim_info.gender,
            'species': sim_info.species
        }

    @classmethod
    def _get_sim_info_creation_data(cls):
        if cls._sim_creation_info.resource_key is not None:
            return cls._get_sim_info_resource_data(
                cls._sim_creation_info.resource_key)
        return {
            'age_range':
            cls._sim_creation_info.age_variant.get_age_range()
            if cls._sim_creation_info.age_variant is not None else None,
            'gender':
            cls._sim_creation_info.gender,
            'species':
            cls._sim_creation_info.species
        }

    @classmethod
    def can_validate_age(cls):
        if cls._sim_creation_info.resource_key is not None and BaseSimInfo is None:
            return False
        return True

    @classmethod
    def matches_creation_data(cls, sim_creator=None, age_min=None):
        sim_info_data = cls._get_sim_info_creation_data()
        if sim_creator is not None:
            if sim_info_data['age_range'] is not None:
                (data_age_min, data_age_max) = sim_info_data['age_range']
                if sim_creator.age < data_age_min or sim_creator.age > data_age_max:
                    return False
            if sim_info_data['gender'] is not None and sim_info_data[
                    'gender'] != sim_creator.gender:
                return False
            if sim_info_data['species'] is not None and sim_info_data[
                    'species'] != sim_creator.species:
                return False
            elif age_min is not None and sim_info_data['age_range'] is not None:
                (data_age_min, data_age_max) = sim_info_data['age_range']
                if data_age_min < age_min:
                    return False
        if age_min is not None and sim_info_data['age_range'] is not None:
            (data_age_min, data_age_max) = sim_info_data['age_range']
            if data_age_min < age_min:
                return False
        return True

    @classmethod
    def add_template_data_to_sim(cls, sim_info, sim_creator=None):
        cls._add_skills(sim_info)
        cls._add_traits(sim_info, sim_creator)
        cls.add_rank(sim_info, sim_creator)
        cls.add_perks(sim_info, sim_creator)
        cls._add_gender_preference(sim_info)
        cls._enroll_in_university(sim_info, sim_creator)
        cls._add_sim_info_fixups(sim_info, sim_creator)

    @classmethod
    def _add_skills(cls, sim_info):
        if not cls._skills.explicit and not cls._skills.random:
            return
        statistic_manager = services.statistic_manager()
        available_skills_types = list(
            set([
                stat
                for stat in statistic_manager.types.values() if stat.is_skill
            ]) - cls._skills.blacklist)
        for skill_data in cls._skills.explicit:
            cls._add_skill_type(sim_info, skill_data, available_skills_types)
        if cls._skills.random:
            num_to_add = cls._skills.random.interval.random_int()
            available_random_skill_data = list(cls._skills.random.choices)
            while num_to_add > 0:
                while available_random_skill_data and available_skills_types:
                    random_skill_data = random.choice(
                        available_random_skill_data)
                    if random_skill_data.skill is not None:
                        available_random_skill_data.remove(random_skill_data)
                    if cls._add_skill_type(sim_info, random_skill_data,
                                           available_skills_types):
                        num_to_add -= 1

    @classmethod
    def _add_skill_type(cls, sim_info, skill_data, available_skills_types):
        skill_type = skill_data.skill
        if skill_type is None:
            skill_type = random.choice(available_skills_types)
        if skill_type is not None:
            if skill_type in available_skills_types:
                available_skills_types.remove(skill_type)
            if skill_type.can_add(sim_info):
                skill_value = skill_type.convert_from_user_value(
                    skill_data.range.random_value())
                sim_info.add_statistic(skill_type, skill_value)
                return True
        return False

    @classmethod
    def _add_traits(cls, sim_info, sim_creator=None):
        trait_tracker = sim_info.trait_tracker
        for trait in tuple(trait_tracker.personality_traits):
            sim_info.remove_trait(trait)
        if sim_creator is not None:
            for trait in sim_creator.traits:
                sim_info.add_trait(trait)
        for trait in cls._traits.explicit:
            sim_info.add_trait(trait)
        if cls._traits.num_random:
            num_to_add = cls._traits.num_random.random_int()
            if num_to_add > 0:
                trait_manager = services.trait_manager()
                available_trait_types = {
                    trait
                    for trait in trait_manager.types.values()
                    if trait.is_personality_trait
                    if not sim_info.has_trait(trait)
                }
                available_trait_types -= cls._traits.blacklist
                available_trait_types -= set(cls._traits.explicit)
                available_trait_types = list(available_trait_types)
                while num_to_add > 0:
                    while available_trait_types:
                        trait = random.choice(available_trait_types)
                        available_trait_types.remove(trait)
                        if not trait_tracker.can_add_trait(trait):
                            continue
                        sim_info.add_trait(trait)
                        num_to_add -= 1

    @classmethod
    def add_rank(cls, sim_info, sim_creator=None, suppress_telemetry=False):
        for rank in cls._ranks:
            ranked_statistic = rank.ranked_statistic
            if ranked_statistic is None:
                continue
            sim_info.commodity_tracker.add_statistic(ranked_statistic)
            stat = sim_info.commodity_tracker.get_statistic(ranked_statistic)
            rank_level = stat.rank_level
            if rank_level == rank.rank:
                continue
            points_needed = stat.points_to_rank(rank.rank)
            stat.refresh_threshold_callback()
            if suppress_telemetry:
                with stat.suppress_level_up_telemetry():
                    stat.set_value(points_needed, from_load=True)
            else:
                stat.set_value(points_needed, from_load=True)

    @classmethod
    def add_perks(cls, sim_info, sim_creator=None, suppress_telemetry=False):
        bucks_tracker = sim_info.get_bucks_tracker(add_if_none=False)
        if bucks_tracker is not None:
            bucks_tracker.clear_bucks_tracker()
        if cls._perks.explicit:
            if bucks_tracker is None:
                bucks_tracker = sim_info.get_bucks_tracker(add_if_none=True)
            for perk in cls._perks.explicit:
                bucks_tracker.unlock_perk(
                    perk, suppress_telemetry=suppress_telemetry)
        if cls._perks.num_random:
            num_to_add = cls._perks.num_random.random_int()
            if num_to_add > 0:
                bucks_perk_manager = services.bucks_perk_manager()
                available_bucks_perk_types = {
                    perk
                    for perk in bucks_perk_manager.types.values()
                    if not bucks_tracker.is_perk_unlocked(perk)
                    if cls._perks.whiteblacklist.test_item(perk)
                }
                available_bucks_perk_types -= set(cls._perks.explicit)
                available_bucks_perk_types = list(available_bucks_perk_types)
                while num_to_add > 0:
                    while available_bucks_perk_types:
                        perk = random.choice(available_bucks_perk_types)
                        available_bucks_perk_types.remove(perk)
                        bucks_tracker.unlock_perk(
                            perk, suppress_telemetry=suppress_telemetry)
                        num_to_add -= 1

    @classmethod
    def _enroll_in_university(cls, sim_info, sim_creator=None):
        if cls._major is not None:
            degree_tracker = sim_info.degree_tracker
            if degree_tracker is None:
                logger.error(
                    "SimInfo {} was created without a degree tracker. Can't assign them a university and major."
                )
                return
            university = University.choose_random_university(
            ) if cls._major.university is None else cls._major.university
            major = University.choose_random_major(
            ) if cls._major.major is None else cls._major.major
            if university is None or major is None:
                logger.error(
                    'Unable to find a major or university (or both) to enroll the Sim in.'
                )
                return
            degree_tracker.process_acceptance(send_telemetry=False)
            if not degree_tracker.is_accepted_degree(university, major):
                degree_tracker.set_accepted_degree(university, major)
            degree_tracker.add_credits(
                cls._major.random_credits_in_range.random_int())
            degree_tracker.enroll(major, university,
                                  cls._major.num_courses_to_enroll_in, [])

    @classmethod
    def _add_sim_info_fixups(cls, sim_info, sim_creator=None):
        fixup_tracker = sim_info.fixup_tracker
        if fixup_tracker is not None:
            for fixup in cls._fixups:
                fixup_tracker.add_fixup(fixup)

    @classmethod
    def _add_gender_preference(cls, sim_info):
        if sims.global_gender_preference_tuning.GlobalGenderPreferenceTuning.enable_autogeneration_same_sex_preference:
            gender_choices = [
                (gender_info.weight, gender_info.gender_preference)
                for gender_info in sims.global_gender_preference_tuning.
                GlobalGenderPreferenceTuning.
                ENABLED_AUTOGENERATION_SAME_SEX_PREFERENCE_WEIGHTS
            ]
        else:
            gender_choices = [
                (gender_info.weight, gender_info.gender_preference)
                for gender_info in sims.global_gender_preference_tuning.
                GlobalGenderPreferenceTuning.GENDER_PREFERENCE_WEIGHTS
            ]
        gender_choice = sims4.random.weighted_random_item(gender_choices)
        for gender_preference in sims.global_gender_preference_tuning.GlobalGenderPreferenceTuning.GENDER_PREFERENCE_MAPPING[
                gender_choice][sim_info.gender]:
            sim_info.add_statistic(gender_preference,
                                   gender_preference.max_value)
class VenueEventDramaNode(VenueEventDramaNodeDisplayMixin, BaseDramaNode):
    GO_TO_VENUE_ZONE_INTERACTION = TunablePackSafeReference(
        description=
        '\n        Reference to the interaction used to travel the Sims to the zone of the venue.\n        ',
        manager=services.get_instance_manager(
            sims4.resources.Types.INTERACTION))
    INSTANCE_TUNABLES = {
        'duration':
        TunableSimMinute(
            description=
            '\n            The duration that this drama node will run for.\n            ',
            minimum=1,
            default=1),
        'zone_director':
        OptionalTunable(
            description=
            '\n            If enabled then this drama node will override the zone director\n            of the lot.\n            ',
            tunable=TunableReference(
                description=
                '\n                The zone director that we will override onto the lot.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ZONE_DIRECTOR))),
        'notification':
        OptionalTunable(
            description=
            '\n            If enabled then we will display a notification when this venue\n            event occurs.\n            ',
            tunable=UiDialogNotification.TunableFactory()),
        'away_notification':
        OptionalTunable(
            description=
            '\n            If enabled then we will display a notification when this venue\n            event occurs if player is not on the lot.\n            Additional Tokens:\n            Zone Name\n            Venue Name\n            ',
            tunable=UiDialogNotification.TunableFactory()),
        'ending_notification':
        OptionalTunable(
            description=
            '\n            If enabled then we will display a notification when this venue\n            event ends if the player is on the current lot that the event is\n            taking place on.\n            ',
            tunable=UiDialogNotification.TunableFactory()),
        'zone_modifier_requirements':
        TunableWhiteBlackList(
            description=
            '\n            A requirement on zone modifiers which must be true on both\n            scheduling and running.\n            ',
            tunable=TunableReference(
                description=
                '\n                Allowed and disallowed zone modifiers\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ZONE_MODIFIER),
                pack_safe=True)),
        'additional_drama_nodes':
        TunableList(
            description=
            '\n            A list of additional drama nodes that we will score and schedule\n            when this drama node is run.  Only 1 drama node is run.\n            ',
            tunable=TunableReference(
                description=
                '\n                A drama node that we will score and schedule when this drama\n                node is run.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.DRAMA_NODE)))
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._duration_alarm_handle = None
        self._zone_id = None
        self._shown_notification = False
        self._additional_nodes_processor = None
        self._duration_override = None

    @classproperty
    def drama_node_type(cls):
        return DramaNodeType.VENUE_EVENT

    @classproperty
    def persist_when_active(cls):
        return True

    @classproperty
    def simless(cls):
        return True

    @property
    def zone_id(self):
        return self._zone_id

    @property
    def is_calendar_deletable(self):
        return False

    def get_calendar_end_time(self):
        return self.get_calendar_start_time() + create_time_span(
            minutes=self.duration)

    @property
    def zone_director_override(self):
        if services.current_zone_id() == self._zone_id:
            return self.zone_director

    def _setup(self, *args, zone_id=None, gsi_data=None, **kwargs):
        result = super()._setup(*args, gsi_data=gsi_data, **kwargs)
        if not result:
            return result
        else:
            self._zone_id = zone_id
            if self._zone_id is None:
                if gsi_data is not None:
                    gsi_data.rejected_nodes.append(
                        GSIRejectedDramaNodeScoringData(
                            type(self),
                            "Failed to setup drama node because it wasn't given a zone id to run in."
                        ))
                return False
        return True

    def cleanup(self, from_service_stop=False):
        super().cleanup(from_service_stop=from_service_stop)
        if self._duration_alarm_handle is not None:
            self._duration_alarm_handle.cancel()
            self._duration_alarm_handle = None

    def _test(self, *args, **kwargs):
        if self._zone_id is None:
            return TestResult(
                False,
                'Cannot run Venue Event Drama Node with no zone id set.')
        zone_modifiers = services.get_zone_modifier_service(
        ).get_zone_modifiers(self._zone_id)
        if not self.zone_modifier_requirements.test_collection(zone_modifiers):
            return TestResult(False,
                              'Incompatible zone modifiers tuned on venue.')
        return super()._test(*args, **kwargs)

    def _end_venue_behavior(self):
        if self.zone_director is not None:
            venue_service = services.venue_service()
            if type(venue_service.get_zone_director()) is self.zone_director:
                if self.ending_notification is not None:
                    dialog = self.ending_notification(
                        services.active_sim_info())
                    dialog.show_dialog()
                venue_service.change_zone_director(
                    venue_service.active_venue.zone_director(), True)
        elif self.ending_notification is not None:
            dialog = self.ending_notification(services.active_sim_info())
            dialog.show_dialog()

    def _show_notification(self):
        if self.notification is None:
            return
        if self._shown_notification:
            return
        dialog = self.notification(services.active_sim_info())
        dialog.show_dialog()
        self._shown_notification = True

    def _run_venue_behavior(self):
        services.venue_service().change_zone_director(self.zone_director(),
                                                      True)
        self._show_notification()

    def _resume_venue_behavior(self):
        self._show_notification()

    def _on_venue_event_complete(self, _):
        if services.current_zone_id() == self._zone_id:
            self._end_venue_behavior()
        services.drama_scheduler_service().complete_node(self._uid)

    def _show_away_notification(self):
        if self.away_notification is None:
            return
        zone_data = services.get_persistence_service().get_zone_proto_buff(
            self._zone_id)
        if zone_data is None:
            return
        venue_tuning_id = build_buy.get_current_venue(self._zone_id)
        venue_manager = services.get_instance_manager(
            sims4.resources.Types.VENUE)
        venue_tuning = venue_manager.get(venue_tuning_id)
        if venue_tuning is None:
            return
        dialog = self.away_notification(services.active_sim_info())
        dialog.show_dialog(additional_tokens=(zone_data.name,
                                              venue_tuning.display_name))

    def _process_scoring_gen(self, timeline):
        try:
            yield from services.drama_scheduler_service(
            ).score_and_schedule_nodes_gen(self.additional_drama_nodes,
                                           1,
                                           zone_id=self._zone_id,
                                           timeline=timeline)
        except GeneratorExit:
            raise
        except Exception as exception:
            logger.exception('Exception while scoring DramaNodes: ',
                             exc=exception,
                             level=sims4.log.LEVEL_ERROR)
        finally:
            self._additional_nodes_processor = None

    def _validate_venue_tuning(self):
        venue_tuning_id = build_buy.get_current_venue(self._zone_id)
        venue_manager = services.get_instance_manager(
            sims4.resources.Types.VENUE)
        venue_tuning = venue_manager.get(venue_tuning_id)
        if venue_tuning is None:
            return False
        elif type(self) not in venue_tuning.drama_node_events:
            return False
        return True

    def _run(self):
        if not self._validate_venue_tuning():
            return DramaNodeRunOutcome.FAILURE
        self._duration_alarm_handle = alarms.add_alarm(
            self, create_time_span(minutes=self.duration),
            self._on_venue_event_complete)
        if services.current_zone_id() == self._zone_id:
            self._run_venue_behavior()
            return DramaNodeRunOutcome.SUCCESS_NODE_INCOMPLETE
        self._show_away_notification()
        if self.additional_drama_nodes:
            sim_timeline = services.time_service().sim_timeline
            self._additional_nodes_processor = sim_timeline.schedule(
                elements.GeneratorElement(self._process_scoring_gen))
        return DramaNodeRunOutcome.SUCCESS_NODE_INCOMPLETE

    def schedule_duration_alarm(self, callback, cross_zone=False):
        if self._duration_override is not None:
            time_span = TimeSpan(self._duration_override)
        else:
            time_span = create_time_span(minutes=self.duration)
        return alarms.add_alarm(self,
                                time_span,
                                callback,
                                cross_zone=cross_zone)

    def should_resume(self):
        return True

    def resume(self):
        if not self.should_resume():
            return
        if services.current_zone_id() == self._zone_id:
            self._resume_venue_behavior()
        self._duration_alarm_handle = self.schedule_duration_alarm(
            self._on_venue_event_complete)

    def _save_custom_data(self, writer):
        writer.write_uint64(ZONE_ID_TOKEN, self._zone_id)
        writer.write_bool(SHOWN_NOTIFICATION_TOKEN, self._shown_notification)
        if self._duration_alarm_handle is not None:
            writer.write_uint64(
                DURATION_TOKEN,
                int(self._duration_alarm_handle.get_remaining_time().in_ticks(
                )))

    def _load_custom_data(self, reader):
        self._zone_id = reader.read_uint64(ZONE_ID_TOKEN, None)
        if self._zone_id is None:
            return False
        self._shown_notification = reader.read_bool(SHOWN_NOTIFICATION_TOKEN,
                                                    False)
        self._duration_override = reader.read_uint64(DURATION_TOKEN, None)
        return True

    def travel_to_venue(self):
        active_sim_info = services.active_sim_info()
        active_sim = active_sim_info.get_sim_instance(
            allow_hidden_flags=ALL_HIDDEN_REASONS_EXCEPT_UNINITIALIZED)
        if active_sim is None:
            return
        if self._zone_id is None:
            logger.error('Failed to travel to venue')
            return
        zone = services.get_zone_manager().get(self._zone_id)
        if zone is None:
            logger.error(
                "The zone of the chosen venue event is not instanced. Travel to the drama node ({})s venue's zone failed.",
                str(self))
            return
        lot_id = zone.lot.lot_id
        if lot_id is None:
            logger.error(
                "Lot of the chosen zone is not instanced. Travel to the drama node ({})s venue's zone failed.",
                str(self))
            return
        pick = PickInfo(pick_type=PickType.PICK_TERRAIN,
                        lot_id=lot_id,
                        ignore_neighborhood_id=True)
        context = interactions.context.InteractionContext(
            active_sim,
            interactions.context.InteractionContext.
            SOURCE_SCRIPT_WITH_USER_INTENT,
            interactions.priority.Priority.High,
            insert_strategy=interactions.context.QueueInsertStrategy.NEXT,
            pick=pick)
        active_sim.push_super_affordance(
            VenueEventDramaNode.GO_TO_VENUE_ZONE_INTERACTION, None, context)

    def load(self, drama_node_proto, schedule_alarm=True):
        super_success = super().load(drama_node_proto,
                                     schedule_alarm=schedule_alarm)
        if not super_success:
            return False
        if not self._validate_venue_tuning():
            return False
        if self.ui_display_type != DramaNodeUiDisplayType.NO_UI:
            services.calendar_service().mark_on_calendar(self)
        return True

    def schedule(self,
                 resolver,
                 specific_time=None,
                 time_modifier=TimeSpan.ZERO,
                 **kwargs):
        success = super().schedule(resolver,
                                   specific_time=specific_time,
                                   time_modifier=time_modifier,
                                   **kwargs)
        if success and self.ui_display_type != DramaNodeUiDisplayType.NO_UI:
            services.calendar_service().mark_on_calendar(self)
        return success

    def create_calendar_entry(self):
        calendar_entry = super().create_calendar_entry()
        calendar_entry.zone_id = self._zone_id
        build_icon_info_msg(
            IconInfoData(
                icon_resource=self._display_data.instance_display_icon),
            self._display_data.instance_display_name, calendar_entry.icon_info)
        calendar_entry.scoring_enabled = False
        return calendar_entry
Example #26
0
 def location_tests(is_outside=True, is_natural_ground=True, is_in_slot=True, is_venue_type=True, is_on_active_lot=True, in_common_area=True, is_fire_allowed=True, is_on_level=True, has_terrain_tag=True, valid_surface_types=True):
     locked_args = {}
     if not is_outside:
         locked_args['is_outside'] = None
     if not is_natural_ground:
         locked_args['is_natural_ground'] = None
     if not is_in_slot:
         locked_args['is_in_slot'] = None
     if not is_venue_type:
         locked_args['is_venue_type'] = None
     if not is_on_active_lot:
         locked_args['is_on_active_lot'] = None
     if not in_common_area:
         locked_args['in_common_area'] = None
     if not is_fire_allowed:
         locked_args['is_fire_allowed'] = None
     if not is_on_level:
         locked_args['is_on_level'] = None
     if not has_terrain_tag:
         locked_args['has_terrain_tag'] = None
     if not valid_surface_types:
         locked_args['valid_surface_types'] = None
     return TunableTuple(is_outside=OptionalTunable(description='\n                If checked, will verify if the subject of the test is outside \n                (no roof over its head) \n                If unchecked, will verify the subject of the test is not \n                outside.\n                ', disabled_name="Don't_Test", tunable=Tunable(bool, True)), has_terrain_tag=OptionalTunable(description='\n                If checked, will verify the subject of the test is currently on\n                the tuned terrain tag.\n                ', disabled_name="Don't_Test", tunable=TunableTuple(description=',\n                    A set of terrain tags required for this test to pass.\n                    ', terrain_tags=TunableEnumSet(description='\n                        A set of terrain tags. Only one of these tags needs to be\n                        present at this location. Although it is not tunable, there\n                        is a threshold weight underneath which a terrain tag will\n                        not appear to be present.\n                        ', enum_type=TerrainTag, enum_default=TerrainTag.INVALID), test_floor_tiles=Tunable(description="\n                        If checked, floor tiles will be tested. Otherwise, \n                        it'll only check the terrain and will ignore the \n                        floor tiles on the terrain.\n                        ", tunable_type=bool, default=False), negate=Tunable(description='\n                        If checked, the test will be inverted. In other words,\n                        the test will fail if at least one tag is detected at\n                        this location.\n                        ', tunable_type=bool, default=False))), is_natural_ground=OptionalTunable(description='\n                If checked, will verify the subject of the test is on natural \n                ground (no floor tiles are under him).\n                Otherwise, will verify the subject of the test is not on \n                natural ground.\n                ', disabled_name="Don't_Test", tunable=Tunable(bool, True)), is_in_slot=OptionalTunable(description='\n                If enabled will test if the object is attacked/deattached to\n                any of possible tuned slots.\n                If you tune a slot type set the test will test if the object \n                is slotted or not slotted into into any of those types. \n                ', disabled_name="Don't_Test", tunable=TunableTuple(description='\n                    Test if an object is current slotted in any of a possible\n                    list of slot types.\n                    Empty slot type set is allowed for testing for slotted or\n                    not slotted only.\n                    ', slot_test_type=TunableVariant(description='\n                        Strategy to test the slots:\n                        Any Slot - is the object in any slot\n                        Surface Slot - is object is in a surface slot\n                        Specific Slot - is the object in specific list of slots\n                        ', any_slot=SlotTestType.TunableFactory(), surface_slot=SurfaceSlotTest.TunableFactory(), specific_slot=SpecificSlotTest.TunableFactory(), default='any_slot'))), is_venue_type=OptionalTunable(description='\n                If checked, will verify if the subject is at a venue of the\n                specified type.\n                ', disabled_name="Don't_Test", tunable=TunableTuple(description='\n                    Venue type required for this test to pass.\n                    ', venue_type=TunablePackSafeReference(description='\n                        Venue type to test against.\n                        ', manager=services.get_instance_manager(sims4.resources.Types.VENUE)), use_source_venue=Tunable(description='\n                        If enabled, the test will test the source venue instead of the active\n                        venue.  For example, the Community Lot instead of the active Marketplace.\n                        Testing the active venue is the default.\n                        ', tunable_type=bool, default=False), negate=Tunable(description='\n                        If enabled, the test will return true if the subject\n                        IS NOT at a venue of the specified type.\n                        ', tunable_type=bool, default=False))), is_on_active_lot=OptionalTunable(description='\n                If disabled the test will not be used.\n                If enabled and checked, the test will pass if the subject is\n                on the active lot. (their center is within the lot bounds)\n                If enabled and not checked, the test will pass if the subject is \n                outside of the active lot.\n                \n                For example, Ask To Leave is tuned with this enabled and checked\n                for the TargetSim. You can only ask someone to leave if they\n                are actually on the active lot, but not if they are wandering\n                around in the open streets.\n                ', disabled_name="Don't_Test", enabled_name='Is_or_is_not_on_active_lot', tunable=TunableTuple(is_or_is_not_on_active_lot=Tunable(description='\n                        If checked then the test will pass if the subject is on\n                        the active lot.\n                        ', tunable_type=bool, default=True), tolerance=TunableVariant(explicit=Tunable(description='\n                            The tolerance from the edge of the lot that the\n                            location test will use in order to determine if the\n                            test target is considered on lot or not.\n                            ', tunable_type=int, default=0), use_default_tolerance=UseDefaultOfflotToleranceFactory(description='\n                            Use the default tuned global offlot tolerance tuned\n                            in objects.components.statistic_component.Default Off Lot.\n                            '), default='explicit'), include_spawn_point=Tunable(description="\n                        If set to true, we will consider the lot's spawn point as part of the active lot.\n                        ", tunable_type=bool, default=False))), in_common_area=OptionalTunable(description='\n                If checked, will verify the subject is in the common area\n                of an apartment.  If unchecked will verify the subject is not.\n                ', disabled_name="Don't_Test", tunable=Tunable(tunable_type=bool, default=True)), is_fire_allowed=OptionalTunable(description="\n                If checked, will verify if fire is possible at the subject's position. \n                If unchecked, will pass if fire is not possible.\n                If not enabled, doesn't care either way.\n                ", disabled_name="Don't_Test", tunable=Tunable(tunable_type=bool, default=True)), is_on_level=OptionalTunable(description="\n                If enabled, we check the participant's current level against\n                the tuned threshold.  In the case of sims in pools, the effective\n                level will be that of the surface of the pool, not the bottom.\n                ", disabled_name="Don't_Test", tunable=TunableThreshold(value=Tunable(int, 0))), valid_surface_types=OptionalTunable(description='\n                If enabled, we will test the surface type of the subject\n                against prohibited or required surface types.\n                ', disabled_name="Don't_Test", enabled_name='Test_Surface_Types', tunable=TunableWhiteBlackList(description='    \n                    Required and Prohibited Surface Types. \n                    ', tunable=TunableEnumEntry(description='\n                        Surface Type the object is placed on.\n                        ', tunable_type=SurfaceType, default=SurfaceType.SURFACETYPE_WORLD, invalid_enums=(SurfaceType.SURFACETYPE_UNKNOWN,)))), locked_args=locked_args)