class TunableSituationJobAndRoles(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {
        'jobs_and_roles':
        TunableMapping(
            description=
            "\n            A mapping between a situation's jobs and default role states.\n            ",
            key_type=TunableReference(
                description=
                '\n                A job created for this situation.\n                ',
                manager=services.situation_job_manager()),
            key_name='Situation Job',
            value_type=TunableTuple(
                number_of_sims_to_find=TunableRange(
                    description=
                    '\n                    The number of sims to find for this job.\n                    ',
                    tunable_type=int,
                    default=1,
                    minimum=1),
                role=TunableReference(
                    description=
                    '\n                    The role state that the sim of this job starts the situation with.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.ROLE_STATE))),
            value_name='Role State Info')
    }
示例#2
0
 def __init__(self, **kwargs):
     super().__init__(
         job_list=TunableMapping(
             description='A list of roles associated with the situation.',
             key_type=TunableReference(services.situation_job_manager(),
                                       description='Job reference'),
             value_type=TunableReference(
                 services.get_instance_manager(
                     sims4.resources.Types.ROLE_STATE),
                 description='Role the job will perform'),
             key_name='job',
             value_name='role'),
         exit_conditions=TunableList(
             TunableTuple(conditions=TunableList(
                 TunableSituationCondition(
                     description=
                     'A condition for a situation or single phase.'),
                 description=
                 'A list of conditions that all must be satisfied for the group to be considered satisfied.'
             )),
             description=
             'A list of condition groups of which if any are satisfied, the group is satisfied.'
         ),
         duration=TunableSimMinute(
             description=
             '\n                                                    How long the phase will last in sim minutes.\n                                                    0 means forever, which should be used on the last phase of the situation.\n                                                    ',
             default=60),
         **kwargs)
示例#3
0
 def start_situation(interaction, situation, user_facing):
     situation_manager = services.get_zone_situation_manager()
     guest_list = situation.get_predefined_guest_list()
     if guest_list is None:
         sim = interaction.sim
         guest_list = SituationGuestList(invite_only=True, host_sim_id=sim.id)
         if situation.targeted_situation is not None:
             target_sim = interaction.get_participant(ParticipantType.PickedSim)
             if target_sim is None:
                 target_sim = interaction.get_participant(ParticipantType.TargetSim)
             target_sim_id = target_sim.id if target_sim is not None else None
             job_assignments = situation.get_prepopulated_job_for_sims(sim, target_sim_id)
             for (sim_id, job_type_id) in job_assignments:
                 job_type = services.situation_job_manager().get(job_type_id)
                 guest_info = SituationGuestInfo.construct_from_purpose(sim_id, job_type, SituationInvitationPurpose.INVITED)
                 guest_list.add_guest_info(guest_info)
         else:
             default_job = situation.default_job()
             target_sims = interaction.get_participants(ParticipantType.PickedSim)
             if target_sims:
                 for sim_or_sim_info in target_sims:
                     guest_info = SituationGuestInfo.construct_from_purpose(sim_or_sim_info.sim_id, default_job, SituationInvitationPurpose.INVITED)
                     guest_list.add_guest_info(guest_info)
             else:
                 target_sim = interaction.get_participant(ParticipantType.TargetSim)
                 if target_sim is not None:
                     guest_info = SituationGuestInfo.construct_from_purpose(target_sim.sim_id, default_job, SituationInvitationPurpose.INVITED)
                     guest_list.add_guest_info(guest_info)
             guest_info = SituationGuestInfo.construct_from_purpose(sim.sim_id, default_job, SituationInvitationPurpose.INVITED)
             guest_list.add_guest_info(guest_info)
     zone_id = interaction.get_participant(ParticipantType.PickedZoneId) or 0
     situation_manager.create_situation(situation, guest_list=guest_list, user_facing=user_facing, zone_id=zone_id)
class CommonSituationState(SituationState, HasTunableFactory):
    FACTORY_TUNABLES = {
        'job_and_role_changes':
        TunableMapping(
            description=
            '\n                A mapping between situation jobs and role states that defines\n                what role states we want to switch to for sims on which jobs\n                when this situation state is entered.\n                ',
            key_type=TunableReference(
                description=
                "\n                    A reference to a SituationJob that we will use to change\n                    sim's role state.\n                    ",
                manager=services.situation_job_manager()),
            key_name='Situation Job',
            value_type=TunableReference(
                description=
                '\n                    The role state that we will switch sims of the linked job\n                    into.\n                    ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ROLE_STATE)),
            value_name='Role State'),
        'allow_join_situation':
        Tunable(
            description=
            '\n                Whether the situation is allowed to join at this state.\n                ',
            tunable_type=bool,
            default=True),
        'time_out':
        OptionalTunable(
            description=
            '\n                How long this state will last before time expired. Please talk to the GPE who implemented the specific\n                situation to see what the state will do on time expired.\n                ',
            tunable=TunableSimMinute(default=15, minimum=1))
    }

    def __init__(self, job_and_role_changes, allow_join_situation, time_out):
        super().__init__()
        self._job_and_role_changes = job_and_role_changes
        self._allow_join_situation = allow_join_situation
        self._time_out = time_out
        self._time_out_string = '' if self._time_out is None else '{}_TIMEOUT'.format(
            self.__class__.__name__)

    def on_activate(self, reader=None):
        super().on_activate(reader)
        self._set_job_role_state()
        if self._time_out is not None:
            self._create_or_load_alarm(self._time_out_string,
                                       self._time_out,
                                       lambda _: self.timer_expired(),
                                       should_persist=True)

    def _set_job_role_state(self):
        for (job, role_state) in self._job_and_role_changes.items():
            self.owner._set_job_role_state(job, role_state)

    def get_all_job_and_role_states(self):
        return self._job_and_role_changes.items()

    def allow_join_situation(self):
        return self._allow_join_situation

    def timer_expired(self):
        pass
示例#5
0
class StrangeWelcomeWagon(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'wait_to_infect_state':
        WaitToInfectSituationState.TunableFactory(
            description=
            '\n            Second state of the situation.  In this state the sims should\n            be waiting to be let into the house.\n            ',
            tuning_group=GroupNames.STATE),
        'infect_state':
        InfectSituationState.TunableFactory(
            description=
            '\n            The third situation state.  In this state everyone parties!\n            ',
            tuning_group=GroupNames.STATE),
        '_player_sim_job':
        TunableReference(
            description=
            '\n            The job for all of the player sims.\n            ',
            manager=services.situation_job_manager())
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(1,
                                   WaitToInfectSituationState,
                                   factory=cls.wait_to_infect_state),
                SituationStateData(2,
                                   InfectSituationState,
                                   factory=cls.infect_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.wait_to_infect_state._tuned_values.
                    job_and_role_changes.items())

    def start_situation(self):
        super().start_situation()
        self._change_state(self.wait_to_infect_state())

    def _issue_requests(self):
        super()._issue_requests()
        request = SelectableSimRequestFactory(self,
                                              callback_data=_RequestUserData(),
                                              job_type=self._player_sim_job,
                                              exclusivity=self.exclusivity)
        self.manager.bouncer.submit_request(request)

    def _on_remove_sim_from_situation(self, sim):
        situation_manager = services.get_zone_situation_manager()
        situation_sim = self._situation_sims.get(sim)
        if situation_sim is not None and situation_sim.current_job_type is not self._player_sim_job:
            situation_manager.make_sim_leave_now_must_run(sim)
        super()._on_remove_sim_from_situation(sim)
示例#6
0
 def deserialize_from_proto(cls, seed_proto):
     situation_type = services.situation_manager().get(seed_proto.situation_type_id)
     if situation_type is None:
         return
     guest_list = SituationGuestList(seed_proto.invite_only, seed_proto.host_sim_id, seed_proto.filter_requesting_sim_id)
     for assignment in seed_proto.assignments:
         job_type = services.situation_job_manager().get(assignment.job_type_id)
         if job_type is None:
             continue
         role_state_type = services.role_state_manager().get(assignment.role_state_type_id)
         guest_info = SituationGuestInfo(assignment.sim_id, job_type, RequestSpawningOption(assignment.spawning_option), BouncerRequestPriority(assignment.request_priority), assignment.expectation_preference, assignment.accept_alternate_sim, SituationCommonBlacklistCategory(assignment.common_blacklist_categories), elevated_importance_override=assignment.elevated_importance_override, reservation=assignment.reservation)
         guest_info._set_persisted_role_state_type(role_state_type)
         guest_list.add_guest_info(guest_info)
     if seed_proto.HasField('duration'):
         duration = seed_proto.duration
     else:
         duration = None
     seed = SituationSeed(situation_type, seed_proto.seed_purpose, seed_proto.situation_id, guest_list, seed_proto.user_facing, duration, seed_proto.zone_id, date_and_time.DateAndTime(seed_proto.start_time), seed_proto.scoring_enabled, main_goal_visiblity=seed_proto.main_goal_visibility, linked_sim_id=seed_proto.linked_sim_id)
     seed._score = seed_proto.score
     if seed_proto.HasField('create_time'):
         seed._travel_time = DateAndTime(seed_proto.create_time)
     for job_data in seed_proto.jobs_and_role_states:
         job_type = services.situation_job_manager().get(job_data.job_type_id)
         if job_type is None:
             continue
         role_state_type = services.role_state_manager().get(job_data.role_state_type_id)
         if role_state_type is None:
             continue
         emotional_loot_actions_type = None
         if job_data.HasField('emotional_loot_actions_type_id'):
             emotional_loot_actions_type = services.action_manager().get(job_data.emotional_loot_actions_type_id)
         seed.add_job_data(job_type, role_state_type, emotional_loot_actions_type)
     if seed_proto.HasField('simple_data'):
         seed.add_situation_simple_data(seed_proto.simple_data.phase_index, seed_proto.simple_data.remaining_phase_time)
     elif seed_proto.HasField('complex_data'):
         complex_data = seed_proto.complex_data
         situation_custom_data = complex_data.situation_custom_data if complex_data.HasField('situation_custom_data') else None
         state_custom_data = complex_data.state_custom_data if complex_data.HasField('state_custom_data') else None
         seed.setup_for_complex_load(situation_custom_data, state_custom_data)
     if seed_proto.HasField('goal_tracker_data'):
         seed._goal_tracker = GoalTrackerSeedling.deserialize_from_proto(seed_proto.goal_tracker_data)
     return seed
示例#7
0
 def start_situation(interaction, situation, user_facing):
     situation_manager = services.get_zone_situation_manager()
     guest_list = situation.get_predefined_guest_list()
     if guest_list is None:
         sim = interaction.sim
         guest_list = SituationGuestList(invite_only=True,
                                         host_sim_id=sim.id)
         if situation.targeted_situation is not None:
             target_sim = interaction.get_participant(
                 ParticipantType.PickedSim)
             if target_sim is None:
                 target_sim = interaction.get_participant(
                     ParticipantType.TargetSim)
             target_sim_id = target_sim.id if target_sim is not None else None
             job_assignments = situation.get_prepopulated_job_for_sims(
                 sim, target_sim_id)
             for (sim_id, job_type_id) in job_assignments:
                 job_type = services.situation_job_manager().get(
                     job_type_id)
                 guest_info = SituationGuestInfo.construct_from_purpose(
                     sim_id, job_type,
                     SituationInvitationPurpose.INVITED)
                 guest_list.add_guest_info(guest_info)
         else:
             default_job = situation.default_job()
             target_sims = interaction.get_participants(
                 ParticipantType.PickedSim)
             if target_sims:
                 for sim_or_sim_info in target_sims:
                     guest_info = SituationGuestInfo.construct_from_purpose(
                         sim_or_sim_info.sim_id, default_job,
                         SituationInvitationPurpose.INVITED)
                     guest_list.add_guest_info(guest_info)
             else:
                 target_sim = interaction.get_participant(
                     ParticipantType.TargetSim)
                 if target_sim is not None:
                     guest_info = SituationGuestInfo.construct_from_purpose(
                         target_sim.sim_id, default_job,
                         SituationInvitationPurpose.INVITED)
                     guest_list.add_guest_info(guest_info)
             guest_info = SituationGuestInfo.construct_from_purpose(
                 sim.sim_id, default_job,
                 SituationInvitationPurpose.INVITED)
             guest_list.add_guest_info(guest_info)
     zone_id = interaction.get_participant(
         ParticipantType.PickedZoneId) or 0
     situation_manager.create_situation(situation,
                                        guest_list=guest_list,
                                        user_facing=user_facing,
                                        zone_id=zone_id)
示例#8
0
 def load(self, load_data):
     super().load(load_data)
     situation_jobs = set()
     situation_job_manager = services.situation_job_manager()
     if load_data.situation_jobs:
         for job_id in load_data.situation_jobs:
             job_type = situation_job_manager.get(job_id)
             if job_type is not None:
                 situation_jobs.add(job_type)
     role_tags = frozenset(load_data.role_tags)
     self.situation_job_test = SituationJobTest(
         participant=load_data.participant_enum,
         negate=load_data.negate,
         situation_jobs=frozenset(situation_jobs),
         role_tags=role_tags)
示例#9
0
class JoinSituationElement(XevtTriggeredElement):
    FACTORY_TUNABLES = {
        'situation_type':
        TunableReference(
            description='\n            The situation to join.\n            ',
            manager=services.situation_manager()),
        'situation_job':
        OptionalTunable(
            description=
            '\n            The situation job that sim will get to while join the situation.\n            ',
            tunable=TunableReference(manager=services.situation_job_manager()),
            disabled_name='use_default_job',
            enabled_name='specify_job',
            disabled_value=DEFAULT),
        'subject':
        TunableEnumFlags(
            description=
            '\n            The participant of who will join the situation.\n            ',
            enum_type=ParticipantType,
            default=ParticipantType.Actor)
    }

    def _do_behavior(self, *args, **kwargs):
        situation_manager = services.get_zone_situation_manager()
        situation = situation_manager.get_situation_by_type(
            self.situation_type)
        if situation is None:
            logger.error(
                'Fail to join situation since cannot find running situation {} for interaction {}',
                self.situation_type,
                self.interaction,
                owner='cjiang')
            return False
        subject = self.interaction.get_participant(self.subject)
        if subject is None or not subject.is_sim:
            logger.error(
                'Fail to join situation since subject {} is not sim for interaction {}',
                self.subject,
                self.interaction,
                owner='cjiang')
            return False
        situation.invite_sim_to_job(subject, job=self.situation_job)
        return True
def create_situation_with_guest_list(situation_type, scoring_enabled, zone_id:int=0, *args, _connection=None):
    if len(args) % 3 != 0:
        sims4.commands.output('Invalid guest list, its length must be a multiple of 3', _connection)
        return False
    situation_manager = services.get_zone_situation_manager()
    client = services.client_manager().get(_connection)
    if client.active_sim is not None:
        host_sim_id = client.active_sim.id
        client.active_sim.household.set_situation_scoring(scoring_enabled)
    else:
        host_sim_id = 0
    guest_list = SituationGuestList(situation_type.force_invite_only, host_sim_id)
    guest_list_is_good = True
    for (job_name_or_key, sim_id_or_name, purpose_name) in zip(args[0::3], args[1::3], args[2::3]):
        job_type = services.situation_job_manager().get(job_name_or_key)
        if job_type is None:
            sims4.commands.output('Invalid job name or key: {}'.format(job_name_or_key), _connection)
            guest_list_is_good = False
        try:
            purpose = SituationInvitationPurpose(purpose_name)
        except ValueError:
            sims4.commands.output('Invalid Purpose: {}. Use INVITED, HIRED, or PREFERRED'.format(purpose_name), _connection)
            guest_list_is_good = False
            continue
        try:
            sim_id = int(sim_id_or_name)
        except (ValueError, TypeError):
            sims4.commands.output('Incorrectly formatted sim_id {}'.format(sim_id_or_name), _connection)
            guest_list_is_good = False
            continue
        guest_info = SituationGuestInfo.construct_from_purpose(sim_id, job_type, purpose)
        guest_list.add_guest_info(guest_info)
    if not guest_list_is_good:
        sims4.commands.output('FAILURE: bad guest list {}.'.format(situation_type), _connection)
        return False
    situation_id = situation_manager.create_situation(situation_type, guest_list=guest_list, zone_id=zone_id, scoring_enabled=scoring_enabled)
    if situation_id is not None:
        sims4.commands.output('Successfully created situation: {}.'.format(situation_id), _connection)
    else:
        sims4.commands.output('Insufficient funds to create situation', _connection)
    return True
示例#11
0
class OrgMemberJobAndRoles(HasTunableSingletonFactory, AutoFactoryInit):
    FACTORY_TUNABLES = {
        'org_member_jobs_and_roles':
        TunableMapping(
            description=
            "\n            A mapping between a situation's jobs and default role states.\n            ",
            key_type=TunableReference(
                description=
                '\n                A job created for this situation.\n                ',
                manager=services.situation_job_manager()),
            key_name='Member Situation Job',
            value_type=TunableTuple(
                role=TunableReference(
                    description=
                    '\n                    The role state that the sim of this job starts the situation with.\n                    ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.
                        ROLE_STATE)),
                organization=TunableReference(
                    description=
                    "\n                    The membership list of this organization fills in the situation's\n                    jobs.\n                    ",
                    manager=services.get_instance_manager(
                        sims4.resources.Types.SNIPPET),
                    class_restrictions='Organization',
                    tuning_group=GroupNames.SITUATION),
                number_of_members=TunableInterval(
                    description=
                    '\n                    The interval defines the range of number of members that need to \n                    fill in the situation job.\n                    ',
                    tunable_type=int,
                    default_lower=2,
                    default_upper=3,
                    minimum=1,
                    tuning_group=GroupNames.SITUATION),
                additional_filters=TunableList(tunable=FilterTermVariant(
                    description=
                    '\n                    Additional filters to be applied to the members request.\n                    \n                    If the existing members pool does not include sims that pass these\n                    filters, the org service will attempt to populate the list with\n                    more members that satisfy these filters.\n                    '
                ),
                                               tuning_group=GroupNames.
                                               SITUATION)),
            value_name='Member Role State and Organization Info')
    }
示例#12
0
class SituationJobAndRoleState:
    FACTORY_TUNABLES = {
        'situation_job':
        TunableReference(
            services.situation_job_manager(),
            description=
            'A reference to a SituationJob that can be performed at this Situation.'
        ),
        'role_state':
        TunableReference(
            services.get_instance_manager(sims4.resources.Types.ROLE_STATE),
            description='A role state the sim assigned to the job will perform'
        )
    }

    def __init__(self, situation_job, role_state):
        self.job = situation_job
        self.role_state = role_state

    def add_to_situation_jobs(self, situation):
        situation._add_job_type(self.job, self.role_state)
class SpookyParty(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'party_time_state':
        _PartyTimeState.TunableFactory(
            description=
            '\n                The first and main state of the situation, this will be where\n                the actual party is ran before the winding down phase.\n                ',
            tuning_group=GroupNames.STATE),
        'wind_down_state':
        _WindDownState.TunableFactory(
            description=
            '\n                Last phase of the situation where desserts will start being\n                served and cleanup will start.\n                ',
            tuning_group=GroupNames.STATE),
        '_default_job':
        TunableReference(
            description=
            '\n                The job for all of the sims invited to the situation.\n                ',
            manager=services.situation_job_manager())
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(1,
                                   _PartyTimeState,
                                   factory=cls.party_time_state),
                SituationStateData(2,
                                   _WindDownState,
                                   factory=cls.wind_down_state))

    @classmethod
    def default_job(cls):
        return cls._default_job

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(
            cls.party_time_state._tuned_values.job_and_role_changes.items())

    def start_situation(self):
        super().start_situation()
        self._change_state(self.party_time_state())
示例#14
0
class PreWelcomeWagon(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'has_front_door_situation_starting_state':
        _HasFrontDoorSituationStartingState.TunableFactory(
            description=
            '\n                The first state of this situation in the case that the lot\n                has a front door.  If it does not then the Has No Front Door\n                Situation Starting State will be started instead.\n                ',
            tuning_group=GroupNames.STATE),
        'has_no_front_door_situation_starting_state':
        _HasNoFrontDoorSituationStartingState.TunableFactory(
            description=
            '\n                The first state of this situation in the case that the lot has\n                no front door.  Sims should be routing to the arrival spawn\n                point.\n                ',
            tuning_group=GroupNames.STATE),
        '_door_knocker_situation_job':
        TunableReference(
            description=
            '\n                The job for the situation door knocker.  This sim will end up\n                being the host for the situation.\n                ',
            manager=services.situation_job_manager()),
        '_fruitcake_bearer_situation_job':
        TunableReference(
            description=
            '\n                The job for the bearing of the vile nastiness known as...\n                \n                \n                ...fruitcake...\n                ',
            manager=services.situation_job_manager()),
        '_other_neighbors_job':
        TunableReference(
            description=
            '\n                The job for all of the other neighbors in the situation.\n                ',
            manager=services.situation_job_manager()),
        '_number_of_neighbors':
        TunableRange(
            description=
            "\n                The number of other neighbors to bring to the situation.  If\n                there aren't enough neighbors then none will be generated to\n                bring.\n                ",
            tunable_type=int,
            default=0,
            minimum=0),
        '_bearer_recipes':
        TunableList(
            description=
            '\n            A list of recipes from which a random selection will\n            be made and used by the fruitcake bearer to generate and carry\n            a craftable into the Pre Welcome Wagon situation.\n            ',
            tunable=TunableReference(
                description=
                '\n                Recipe that will be brought in during the Pre Welcome Wagon \n                situation by the fruitcake-bearer.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.RECIPE)),
            minlength=1),
        '_welcome_wagon_situation':
        TunableReference(
            description=
            '\n                The actual welcome wagon situation that we want to start once\n                we have actually gotten the Sims to where we want them to be.\n                ',
            manager=services.get_instance_manager(
                sims4.resources.Types.SITUATION))
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classproperty
    def sets_welcome_wagon_flag(cls):
        return True

    @classmethod
    def _states(cls):
        return (SituationStateData(
            1,
            _HasFrontDoorSituationStartingState,
            factory=cls.has_front_door_situation_starting_state),
                SituationStateData(
                    2,
                    _HasNoFrontDoorSituationStartingState,
                    factory=cls.has_no_front_door_situation_starting_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.has_front_door_situation_starting_state._tuned_values.
                    job_and_role_changes.items())

    @classmethod
    def get_predefined_guest_list(cls):
        active_sim_info = services.active_sim_info()
        door_knocker_results = services.sim_filter_service().submit_filter(
            cls._door_knocker_situation_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not door_knocker_results:
            return
        door_knocker = random.choice(door_knocker_results)
        guest_list = SituationGuestList(
            invite_only=True,
            host_sim_id=door_knocker.sim_info.sim_id,
            filter_requesting_sim_id=active_sim_info.sim_id)
        guest_list.add_guest_info(
            SituationGuestInfo(door_knocker.sim_info.sim_id,
                               cls._door_knocker_situation_job,
                               RequestSpawningOption.DONT_CARE,
                               BouncerRequestPriority.EVENT_VIP,
                               expectation_preference=True))
        blacklist = set()
        blacklist.add(door_knocker.sim_info.sim_id)
        fruitcake_bearer_results = services.sim_filter_service().submit_filter(
            cls._fruitcake_bearer_situation_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            blacklist_sim_ids=blacklist,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not fruitcake_bearer_results:
            return guest_list
        fruitcake_bearer = random.choice(fruitcake_bearer_results)
        guest_list.add_guest_info(
            SituationGuestInfo(fruitcake_bearer.sim_info.sim_id,
                               cls._fruitcake_bearer_situation_job,
                               RequestSpawningOption.DONT_CARE,
                               BouncerRequestPriority.EVENT_VIP,
                               expectation_preference=True))
        blacklist.add(fruitcake_bearer.sim_info.sim_id)
        other_neighbors_results = services.sim_filter_service().submit_filter(
            cls._other_neighbors_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            blacklist_sim_ids=blacklist,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not other_neighbors_results:
            return guest_list
        if len(other_neighbors_results) > cls._number_of_neighbors:
            neighbors = random.sample(other_neighbors_results,
                                      cls._number_of_neighbors)
        else:
            neighbors = other_neighbors_results
        for neighbor in neighbors:
            guest_list.add_guest_info(
                SituationGuestInfo(neighbor.sim_info.sim_id,
                                   cls._other_neighbors_job,
                                   RequestSpawningOption.DONT_CARE,
                                   BouncerRequestPriority.EVENT_VIP,
                                   expectation_preference=True))
        return guest_list

    def __init__(self, *arg, **kwargs):
        super().__init__(*arg, **kwargs)
        reader = self._seed.custom_init_params_reader
        if reader is None:
            self._fruitcake_id = None
        else:
            self._fruitcake_id = reader.read_uint64(FRUITCAKE_TOKEN, None)

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        if self._fruitcake_id is not None:
            writer.write_uint64(FRUITCAKE_TOKEN, self._fruitcake_id)

    def start_situation(self):
        super().start_situation()
        if services.get_door_service().has_front_door():
            self._change_state(self.has_front_door_situation_starting_state())
        else:
            self._change_state(
                self.has_no_front_door_situation_starting_state())

    def create_welcome_wagon(self):
        situation_manager = services.get_zone_situation_manager()
        if not situation_manager.is_user_facing_situation_running():
            situation_manager.create_situation(
                self._welcome_wagon_situation,
                guest_list=self._guest_list.clone(),
                user_facing=True,
                scoring_enabled=False)
        active_household = services.active_household()
        active_household.needs_welcome_wagon = False
        self._self_destruct()
示例#15
0
class StrangePreWelcomeWagon(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'has_front_door_situation_starting_state':
        HasFrontDoorStrangeSituationStartingState.TunableFactory(
            description=
            '\n            The first state of this situation in the case that the lot\n            has a front door.  If it does not then the Has No Front Door\n            Situation Starting State will be started instead.\n            ',
            tuning_group=GroupNames.STATE),
        'has_no_front_door_situation_starting_state':
        HasNoFrontDoorStrangeSituationStartingState.TunableFactory(
            description=
            '\n            The first state of this situation in the case that the lot has\n            no front door.  Sims should be routing to the arrival spawn\n            point.\n            ',
            tuning_group=GroupNames.STATE),
        '_door_knocker_situation_job':
        TunableReference(
            description=
            '\n            The job for the situation door knocker.  This sim will end up\n            being the host for the situation.\n            ',
            manager=services.situation_job_manager()),
        '_fruitcake_bearer_situation_job':
        TunableReference(
            description=
            '\n            The job for the bearing of the vile nastiness known as...\n            \n            \n            ...fruitcake...\n            ',
            manager=services.situation_job_manager()),
        '_other_infected_job':
        TunableReference(
            description=
            '\n            The job for all of the other infected in the situation.\n            ',
            manager=services.situation_job_manager()),
        '_extra_infected':
        TunableRange(
            description=
            '\n            The number of additional infected Sims to bring.\n            ',
            tunable_type=int,
            default=1,
            minimum=1),
        '_fruitcake_recipe':
        TunableReference(
            description=
            '\n            A recipe for the revolting food product commonly known as...\n            \n            \n            ...fruitcake...\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.RECIPE)),
        '_welcome_wagon_situation':
        TunableReference(
            description=
            '\n            The actual welcome wagon situation that we want to start once\n            we have actually gotten the Sims to where we want them to be.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.SITUATION)),
        '_possession_source':
        TunableBuffReference(
            description=
            "\n            Possession buff that keeps the Sims possessed even after the\n            situation's end.\n            "
        )
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classproperty
    def sets_welcome_wagon_flag(cls):
        return True

    @classmethod
    def _states(cls):
        return (SituationStateData(
            1,
            HasFrontDoorStrangeSituationStartingState,
            factory=cls.has_front_door_situation_starting_state),
                SituationStateData(
                    2,
                    HasNoFrontDoorStrangeSituationStartingState,
                    factory=cls.has_no_front_door_situation_starting_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.has_front_door_situation_starting_state._tuned_values.
                    job_and_role_changes.items())

    @classmethod
    def get_predefined_guest_list(cls):
        active_sim_info = services.active_sim_info()
        door_knocker_results = services.sim_filter_service(
        ).submit_matching_filter(
            sim_filter=cls._door_knocker_situation_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        door_knocker = door_knocker_results[0]
        guest_list = SituationGuestList(
            invite_only=True,
            host_sim_id=door_knocker.sim_info.sim_id,
            filter_requesting_sim_id=active_sim_info.sim_id)
        guest_list.add_guest_info(
            SituationGuestInfo(door_knocker.sim_info.sim_id,
                               cls._door_knocker_situation_job,
                               RequestSpawningOption.DONT_CARE,
                               BouncerRequestPriority.EVENT_VIP,
                               expectation_preference=True))
        blacklist = set()
        blacklist.add(door_knocker.sim_info.sim_id)
        fruitcake_bearer_results = services.sim_filter_service(
        ).submit_matching_filter(
            sim_filter=cls._fruitcake_bearer_situation_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            blacklist_sim_ids=blacklist,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        fruitcake_bearer = fruitcake_bearer_results[0]
        guest_list.add_guest_info(
            SituationGuestInfo(fruitcake_bearer.sim_info.sim_id,
                               cls._fruitcake_bearer_situation_job,
                               RequestSpawningOption.DONT_CARE,
                               BouncerRequestPriority.EVENT_VIP,
                               expectation_preference=True))
        blacklist.add(fruitcake_bearer.sim_info.sim_id)
        guaranteed_infected_results = services.sim_filter_service(
        ).submit_matching_filter(sim_filter=cls._other_infected_job.filter,
                                 callback=None,
                                 requesting_sim_info=active_sim_info,
                                 allow_yielding=False,
                                 blacklist_sim_ids=blacklist,
                                 gsi_source_fn=cls.get_sim_filter_gsi_name)
        guaranteed_infected = guaranteed_infected_results[0]
        guest_list.add_guest_info(
            SituationGuestInfo(guaranteed_infected.sim_info.sim_id,
                               cls._other_infected_job,
                               RequestSpawningOption.DONT_CARE,
                               BouncerRequestPriority.EVENT_VIP,
                               expectation_preference=True))
        other_infected = services.sim_filter_service().submit_filter(
            sim_filter=cls._other_infected_job.filter,
            callback=None,
            requesting_sim_info=active_sim_info,
            allow_yielding=False,
            blacklist_sim_ids=blacklist,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not other_infected:
            return guest_list
        if len(other_infected) > cls._extra_infected - 1:
            infected_to_come = random.sample(other_infected,
                                             cls._extra_infected - 1)
        else:
            infected_to_come = other_infected
        for infected in infected_to_come:
            guest_list.add_guest_info(
                SituationGuestInfo(infected.sim_info.sim_id,
                                   cls._other_infected_job,
                                   RequestSpawningOption.DONT_CARE,
                                   BouncerRequestPriority.EVENT_VIP,
                                   expectation_preference=True))
        return guest_list

    def __init__(self, *arg, **kwargs):
        super().__init__(*arg, **kwargs)
        reader = self._seed.custom_init_params_reader
        if reader is None:
            self._fruitcake_id = None
        else:
            self._fruitcake_id = reader.read_uint64(FRUITCAKE_TOKEN, None)

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        if self._fruitcake_id is not None:
            writer.write_uint64(FRUITCAKE_TOKEN, self._fruitcake_id)

    @property
    def _bearer_recipes(self):
        return (self._fruitcake_recipe, )

    def start_situation(self):
        super().start_situation()
        if services.get_door_service().has_front_door():
            self._change_state(self.has_front_door_situation_starting_state())
        else:
            self._change_state(
                self.has_no_front_door_situation_starting_state())

    def create_welcome_wagon(self):
        situation_manager = services.get_zone_situation_manager()
        if not situation_manager.is_user_facing_situation_running():
            situation_manager.create_situation(
                self._welcome_wagon_situation,
                guest_list=self._guest_list.clone(),
                user_facing=True,
                scoring_enabled=False)
        active_household = services.active_household()
        active_household.needs_welcome_wagon = False
        self._self_destruct()
class NPCHostedBirthdayParty(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'preparation_situation_state':
        _BirthdayPreperationSituationState.TunableFactory(
            description=
            '\n                The first state of this situation.  In this state sims should\n                be socializing and the caterer should be preparing the birthday\n                cake.\n                \n                All jobs and role states should be defined in this situation\n                state.\n                ',
            tuning_group=GroupNames.STATE),
        'age_up_host_situation_state':
        _AgeUpHostSituationState.TunableFactory(
            description=
            '\n                Second state of the situation.  This state should start when\n                the caterer has finished setting up the birthday cake.  This\n                situation state will have the host sim try and age up with the\n                birthday cake.\n                ',
            tuning_group=GroupNames.STATE),
        'age_up_host_backup_situation_state':
        TunableTuple(
            description=
            '\n                Information related to the Age Up Host Backup Situation State.\n                ',
            situation_state=_AgeUpHostBackupSituationState.TunableFactory(
                description=
                "\n                    Backup Situation State.  Hopefully this will never have to be\n                    used.  In the case that the caterer is never able to make a\n                    cake or the host sim isn't able to run the age up interaction\n                    on the cake then this state will be entered.  Within this\n                    state the host sim will attempt to be forced to age up.\n                    "
            ),
            time_out=TunableSimMinute(
                description=
                '\n                    The amount of time since the beginning of the situation\n                    that we will be put into the Age Up Host Backup Situation\n                    State if we are not already in the Post Age Up Situation\n                    State.\n                    ',
                default=1,
                minimum=1),
            tuning_group=GroupNames.STATE),
        'post_age_up_situation_state':
        _PostAgeUpSituationState.TunableFactory(
            description=
            '\n                The third situation state.  This state should encompass all of\n                the situation behavior after the sim has aged up and will\n                continue till the end of the party.\n                ',
            tuning_group=GroupNames.STATE),
        '_default_job':
        TunableReference(
            description=
            '\n                The default job for Sims in this situation\n                ',
            manager=services.situation_job_manager(),
            allow_none=True)
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(1,
                                   _BirthdayPreperationSituationState,
                                   factory=cls.preparation_situation_state),
                SituationStateData(2,
                                   _AgeUpHostSituationState,
                                   factory=cls.age_up_host_situation_state),
                SituationStateData(
                    3,
                    _AgeUpHostBackupSituationState,
                    factory=cls.age_up_host_backup_situation_state.
                    situation_state),
                SituationStateData(4,
                                   _PostAgeUpSituationState,
                                   factory=cls.post_age_up_situation_state))

    @classmethod
    def default_job(cls):
        return cls._default_job

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.preparation_situation_state._tuned_values.
                    job_and_role_changes.items())

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

    def start_situation(self):
        super().start_situation()
        self._change_state(self.preparation_situation_state())
        self._setup_backup_alarm_handle()

    def load_situation(self):
        result = super().load_situation()
        if result:
            self._setup_backup_alarm_handle()
        return result

    def _change_to_backup_state(self, _):
        if type(self._cur_state) is _PostAgeUpSituationState:
            return
        self._change_state(
            self.age_up_host_backup_situation_state.situation_state())

    def _setup_backup_alarm_handle(self):
        if type(self._cur_state) is _PostAgeUpSituationState:
            return
        backup_time = self._start_time + create_time_span(
            minutes=self.age_up_host_backup_situation_state.time_out)
        now = services.time_service().sim_now
        time_till_backup_state = backup_time - now
        if time_till_backup_state.in_ticks() <= 0:
            self._change_state(
                self.age_up_host_backup_situation_state.situation_state())
            return
        self._backup_timeout_alarm_handle = alarms.add_alarm(
            self, time_till_backup_state, self._change_to_backup_state)
示例#17
0
class _DoStuffState(CommonSituationState):
    FACTORY_TUNABLES = {
        'job_and_role_gym_stuff':
        TunableMapping(
            description=
            '\n                A mapping between situation jobs and role states that defines\n                what role states we want to switch to for sims on which jobs\n                when this situation state is entered.\n                ',
            key_type=TunableReference(
                description=
                "\n                    A reference to a SituationJob that we will use to change\n                    sim's role state.\n                    ",
                manager=services.situation_job_manager(),
                pack_safe=True),
            key_name='Situation Job',
            value_type=TunableReference(
                description=
                '\n                    The role state that we will switch sims of the linked job\n                    into.\n                    ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.ROLE_STATE),
                pack_safe=True),
            value_name='Role State'),
        'do_stuff_timeout':
        OptionalTunable(
            description=
            '\n                Optional tunable for when to end the Do Stuff state. \n    \n                If this is enabled then the Do Stuff state will eventually time\n                out and end the situation.\n                \n                If this is disabled the situation will just stay in the Do Stuff\n                state forever.\n                ',
            tunable=TunableTuple(
                description='\n                \n                    ',
                min_time=TunableSimMinute(
                    description=
                    '\n                        The length of time to wait before advancing to the\n                        Change Clothes state.\n                        ',
                    default=60),
                max_time=TunableSimMinute(
                    description=
                    '\n                        The maximum time a visitor will spend on the relaxation\n                        venue as a guest.\n                        ',
                    default=60))),
        'locked_args': {
            'job_and_role_changes': frozendict()
        }
    }

    def __init__(self, job_and_role_gym_stuff, do_stuff_timeout, **kwargs):
        super().__init__(**kwargs)
        self._job_and_role_gym_stuff = job_and_role_gym_stuff
        self._do_stuff_timeout = do_stuff_timeout

    def on_activate(self, reader=None):
        super().on_activate(reader)
        if self._do_stuff_timeout is not None:
            duration = random.uniform(self._do_stuff_timeout.min_time,
                                      self._do_stuff_timeout.max_time)
            self._create_or_load_alarm(DO_STUFF_TIMEOUT,
                                       duration,
                                       lambda _: self.timer_expired(),
                                       should_persist=True,
                                       reader=reader)

    def _set_job_role_state(self):
        for (job, role_state) in self._job_and_role_gym_stuff.items():
            if job is not None:
                if role_state is not None:
                    self.owner._set_job_role_state(job, role_state)

    def _get_remaining_time_for_gsi(self):
        return self._get_remaining_alarm_time(DO_STUFF_TIMEOUT)

    def timer_expired(self):
        self.owner._self_destruct()
def create_situation_with_guest_list(situation_type,
                                     scoring_enabled,
                                     zone_id: int = 0,
                                     *args,
                                     _connection=None):
    if len(args) % 3 != 0:
        sims4.commands.output(
            'Invalid guest list, its length must be a multiple of 3',
            _connection)
        return False
    situation_manager = services.get_zone_situation_manager()
    client = services.client_manager().get(_connection)
    if client.active_sim is not None:
        host_sim_id = client.active_sim.id
        client.active_sim.household.set_situation_scoring(scoring_enabled)
    else:
        host_sim_id = 0
    guest_list = SituationGuestList(situation_type.force_invite_only,
                                    host_sim_id)
    guest_list_is_good = True
    for (job_name_or_key, sim_id_or_name,
         purpose_name) in zip(args[0::3], args[1::3], args[2::3]):
        job_type = services.situation_job_manager().get(job_name_or_key)
        if job_type is None:
            sims4.commands.output(
                'Invalid job name or key: {}'.format(job_name_or_key),
                _connection)
            guest_list_is_good = False
        try:
            purpose = SituationInvitationPurpose(purpose_name)
        except ValueError:
            sims4.commands.output(
                'Invalid Purpose: {}. Use INVITED, HIRED, or PREFERRED'.format(
                    purpose_name), _connection)
            guest_list_is_good = False
            continue
        try:
            sim_id = int(sim_id_or_name)
        except (ValueError, TypeError):
            sims4.commands.output(
                'Incorrectly formatted sim_id {}'.format(sim_id_or_name),
                _connection)
            guest_list_is_good = False
            continue
        guest_info = SituationGuestInfo.construct_from_purpose(
            sim_id, job_type, purpose)
        guest_list.add_guest_info(guest_info)
    if not guest_list_is_good:
        sims4.commands.output(
            'FAILURE: bad guest list {}.'.format(situation_type), _connection)
        return False
    situation_id = situation_manager.create_situation(
        situation_type,
        guest_list=guest_list,
        zone_id=zone_id,
        scoring_enabled=scoring_enabled)
    if situation_id is not None:
        sims4.commands.output(
            'Successfully created situation: {}.'.format(situation_id),
            _connection)
    else:
        sims4.commands.output('Insufficient funds to create situation',
                              _connection)
    return True
class RangerGreetingSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'ranger_job': TunableReference(description='\n                The situation job that will be given to the ranger.\n                ', manager=services.situation_job_manager()), 'ranger_greet_lot_role_state': TunableReference(description='\n                The role state that will be given to the ranger npc on the\n                initial creation of the situation in order for the ranger to\n                go and greet the sims on the new household moving in.\n                ', manager=services.get_instance_manager(sims4.resources.Types.ROLE_STATE)), 'ranger_greet_lot_state_change_interaction': situations.situation_complex.TunableInteractionOfInterest(description='\n                The interaction that when run by the ranger npc will switch the\n                situation state to the wait around state.\n                '), 'ranger_wait_role_state': TunableReference(description='\n                The role state that will be given to the ranger npc after they\n                have completed their greet interaction.\n                ', manager=services.get_instance_manager(sims4.resources.Types.ROLE_STATE))}
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _RangerGreetLotSituationState), SituationStateData(2, _RangerWaitSituationState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.ranger_job, cls.ranger_greet_lot_role_state)]

    @classmethod
    def default_job(cls):
        return cls.ranger_job

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

    def start_situation(self):
        super().start_situation()
        self._change_state(_RangerGreetLotSituationState())

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1
示例#20
0
class IslandWelcomeWagon(WelcomeWagon):
    INSTANCE_TUNABLES = {'_lei_carrier_situation_job': TunableReference(description='\n            The job for the lei carrier.\n            ', manager=services.situation_job_manager())}
示例#21
0
class WelcomeWagon(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'wait_to_be_let_in_state':
        _WaitToBeLetInState.TunableFactory(
            description=
            '\n                Second state of the situation.  In this state the sims should\n                be waiting to be let into the house.\n                ',
            tuning_group=GroupNames.STATE),
        'party_situation_state':
        _PartySituationState.TunableFactory(
            description=
            '\n                The third situation state.  In this state everyone parties!\n                ',
            tuning_group=GroupNames.STATE),
        '_door_knocker_situation_job':
        TunableReference(
            description=
            '\n                The job for the situation door knocker.  This sim will end up\n                being the host for the situation.\n                ',
            manager=services.situation_job_manager()),
        '_fruitcake_bearer_situation_job':
        TunableReference(
            description=
            '\n                The job for the bearing of the vile nastiness known as...\n                \n                \n                ...fruitcake...\n                ',
            manager=services.situation_job_manager()),
        '_other_neighbors_job':
        TunableReference(
            description=
            '\n                The job for all of the other neighbors in the situation.\n                ',
            manager=services.situation_job_manager()),
        '_player_sim_job':
        TunableReference(
            description=
            '\n                The job for all of the player sims.\n                ',
            manager=services.situation_job_manager())
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(3,
                                   _WaitToBeLetInState,
                                   factory=cls.wait_to_be_let_in_state),
                SituationStateData(4,
                                   _PartySituationState,
                                   factory=cls.party_situation_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(cls.wait_to_be_let_in_state._tuned_values.
                    job_and_role_changes.items())

    def start_situation(self):
        super().start_situation()
        self._change_state(self.wait_to_be_let_in_state())

    def _issue_requests(self):
        super()._issue_requests()
        request = SelectableSimRequestFactory(self,
                                              callback_data=_RequestUserData(),
                                              job_type=self._player_sim_job,
                                              exclusivity=self.exclusivity)
        self.manager.bouncer.submit_request(request)
示例#22
0
class JobAssignedSubSituationState(SubSituationState):
    FACTORY_TUNABLES = {
        'job_mapping':
        TunableMapping(
            description=
            "\n            A mapping between sub-situation job and the owner situation job.\n            When creating the guest list for the sub-situation, job pairings will\n            inform which Sim is pulled in to fill the sub-situation's job. \n            ",
            key_type=TunableReference(
                description=
                '\n                A job created for the sub-situation.\n                ',
                manager=services.situation_job_manager()),
            key_name='Sub Situation Job',
            value_type=TunableTuple(
                reuse_type=TunableEnumEntry(
                    description=
                    '\n                    Rule to determine how the sub-situation job should be filled\n                    on next creation.\n                    \n                    If Same, then on repeated iterations of the sub-situation, its job will\n                    be filled by the sim that had filled it in the past iterations.\n                    \n                    If Different, then on repeated iterations of the sub-situation, its job\n                    will be filled by a sim other than those that filled it in past iterations.\n                    ',
                    tunable_type=ReUseType,
                    default=ReUseType.SAME_SIM),
                job=TunableReference(
                    description=
                    "\n                    A job from the owner situation. Sims with this job will be\n                    used as a source for the sub-situation's job.\n                    ",
                    manager=services.situation_job_manager())),
            value_name='Sub Situation Job Constraint',
            minlength=1)
    }

    @classmethod
    def _verify_tuning_callback(cls):
        if cls.sub_situation.default_job not in cls.job_mapping.values():
            logger.error(
                "Sub-situation ({})'s default job ({}) is not in the situation's job constraint mapping. The sub-situation will not be created.",
                str(cls.sub_situation), str(cls.sub_situation.default_job))

    def __init__(self, job_mapping, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.job_mapping = job_mapping
        self._used_sims = defaultdict(list)

    def _filter_guest_with_constraint(self, sub_sit_job, job_constraint,
                                      filtered_guests):
        guests_had_job = self._used_sims.get(sub_sit_job, [])
        if not guests_had_job:
            return random.choice(filtered_guests)
        if job_constraint == ReUseType.DIFFERENT_SIM:
            valid_guests = [
                filtered_guest for filtered_guest in filtered_guests
                if filtered_guest not in guests_had_job
            ]
            if len(valid_guests) < 1:
                owner_guest = random.choice(guests_had_job)
                self._used_sims[sub_sit_job].clear()
                return owner_guest
            return random.choice(valid_guests)
        if job_constraint == ReUseType.SAME_SIM:
            return random.choice(guests_had_job)
        logger.error(
            'Tuned ReUse Type ({}) is not handled. No Sim for sub-situation job ({}) was found.',
            str(job_constraint), str(sub_sit_job))

    def get_guest_by_job_constraint(self, sub_sit_job, job_constraint_info,
                                    owner_guests):
        owner_guests_with_job = [
            guest_info.sim_id for guest_info in
            owner_guests._job_type_to_guest_infos.get(job_constraint_info.job)
        ]
        if not owner_guests_with_job:
            logger.error(
                'No guests of owning situation ({}) were found to have the job ({}) of the sub-situation ({}) job constraint.',
                str(self.owner), str(job_constraint_info.job),
                str(self.sub_situation))
            return
        return self._filter_guest_with_constraint(
            sub_sit_job, job_constraint_info.reuse_type, owner_guests_with_job)

    def get_guest_list(self):
        owner_guests = self.owner.guest_list
        jobs = self.sub_situation.get_tuned_jobs()
        guest_list = SituationGuestList(invite_only=True)
        for job in jobs:
            job_constraint_info = self.job_mapping.get(job)
            if job_constraint_info is None:
                logger.error(
                    "Failed to find job mapping constraint for sub-situation's default job ({}). Tune ({}) constraint in Activity State.",
                    str(job), str(job))
                return
            owner_guest_id = self.get_guest_by_job_constraint(
                job, job_constraint_info, owner_guests)
            if owner_guest_id is None:
                logger.error(
                    'Failed to find guest for situation ({}). Situation will not run.',
                    str(self.sub_situation))
                return
            guest_list.add_guest_info(
                SituationGuestInfo(owner_guest_id, job,
                                   RequestSpawningOption.DONT_CARE,
                                   BouncerRequestPriority.EVENT_VIP))
            self._used_sims[job].append(owner_guest_id)
        return guest_list

    def start_situation(self):
        guest_list = self.get_guest_list()
        if guest_list is None:
            logger.error(
                'Failed to start sub-situation. Transitioning to next phase ({}).',
                str(self.owner.end_state))
            self._on_sub_situation_end()
        self._sub_situation_id = services.get_zone_situation_manager(
        ).create_situation(self.sub_situation,
                           guest_list=guest_list,
                           user_facing=False)
        super().start_situation()
示例#23
0
class SituationJob(HasDependentTunableReference,
                   metaclass=HashedTunedInstanceMetaclass,
                   manager=services.situation_job_manager()):
    __qualname__ = 'SituationJob'
    CHANGE_OUTFIT_INTERACTION = interactions.base.super_interaction.SuperInteraction.TunableReference(
        description=
        '\n        A reference that should be tuned to an interaction that will just set\n        sim to their default outfit.\n        '
    )
    INSTANCE_TUNABLES = {
        'display_name':
        TunableLocalizedString(
            description=
            '\n                Localized name of this job. This name is displayed in the situation\n                creation UI where the player is making selection of sims that belong\n                to a specific job. E.g. "Guest", "Bride or Groom", "Bartender".\n                \n                Whenever you add a display name, evaluate whether your design \n                needs or calls out for a tooltip_name.\n                ',
            tuning_group=GroupNames.UI),
        'tooltip_name':
        TunableLocalizedString(
            description=
            '\n                Localized name of this job that is displayed when the player hovers\n                on the sim while the situation is in progress. If this field is absent, \n                there will be no tooltip on the sim.\n                \n                This helps distinguish the cases where we want to display "Bride or Groom" \n                in the situation creation UI but only "Bride" or "Groom" on the \n                sim\'s tooltip when the player is playing with the situation. \n                ',
            tuning_group=GroupNames.UI),
        'icon':
        TunableResourceKey(
            description=
            '\n                Icon to be displayed for the job of the Sim\n                ',
            default=None,
            needs_tuning=True,
            resource_types=sims4.resources.CompoundTypes.IMAGE,
            tuning_group=GroupNames.UI),
        'job_description':
        TunableLocalizedString(
            description=
            '\n                Localized description of this job\n                ',
            tuning_group=GroupNames.UI),
        'tests':
        event_testing.tests.TunableTestSet(),
        'sim_auto_invite':
        TunableInterval(
            description=
            "\n                On situation start it will select a random number of sims in this interval.\n                It will automatically add npcs to the situation so that it has at least\n                that many sims in this job including those the player\n                invites/hires. If the player invites/hires more than the auto\n                invite number, no npcs will be automatically added.\n                \n                Auto invite sims are considered to be invited, so they will be\n                spawned for invite only situations too. For player initiated\n                situations you probably want to set this 0. It is really meant\n                for commercial venues.\n                \n                You can use Churn tuning on this job if you want the number of\n                sims to vary over time. Churn tuning will override this one.\n                \n                For example, an ambient bar situation would have a high auto\n                invite number for the customer job because we want many sims in\n                the bar but the player doesn't invite or hire anybody for an\n                ambient situation.\n                \n                A date would have 0 for this for all jobs because the situation\n                would never spawn anybody to fill jobs, the player would have\n                to invite them all.\n                ",
            tunable_type=int,
            default_lower=0,
            default_upper=0,
            minimum=0),
        'sim_auto_invite_allow_instanced_sim':
        Tunable(
            description=
            '\n                If checked will allow instanced sims to be assigned this job\n                to fulfill auto invite spots instead of forcing the spawning\n                of new sims.\n                \n                NOTE: YOU PROBABLY WANT TO LEAVE THIS AS UNCHECKED.  PLEASE\n                CONSULT A GPE IF YOU PLAN ON TUNING IT.\n                ',
            tunable_type=bool,
            default=False),
        'sim_count':
        TunableInterval(
            description=
            '\n                The number of Sims the player is allowed to invite or hire for\n                this job.  The lower bound is the required number of sims, the\n                upper bound is the maximum.\n                \n                This only affects what the player can do in the Plan an Event UI.\n                It has no affect while the situation is running.\n                ',
            tunable_type=int,
            default_lower=1,
            default_upper=1,
            minimum=0),
        'churn':
        OptionalTunable(
            description=
            'If enabled, produces churn or turnover\n                in the sims holding this job. Periodically sims in the job will leave\n                the lot and other sims will come to fill the job. \n                \n                When a situation is first started it will automatically invite a\n                number of sims appropriate for the time of day. This supercedes\n                sim_auto_invite.\n                \n                This is primarily for commercial venue customers.\n                This is NOT compatible with Sim Shifts.\n                ',
            tunable=SituationJobChurn.TunableFactory(),
            display_name='Sim Churn'),
        'sim_shifts':
        OptionalTunable(
            description=
            'If enabled, creates shifts of\n                sims who replace the sims currently in the job.\n                \n                When a situation is first started it will automatically invite a\n                number of sims appropriate for the time of day. This supercedes\n                sim_auto_invite.\n                \n                This is primarily intended for commercial venue employees.\n                This is NOT compatible with Sim Churn.\n                ',
            tunable=SituationJobShifts.TunableFactory()),
        'goal_scoring':
        Tunable(
            description=
            '\n                The score for completing a goal\n                ',
            tunable_type=int,
            default=1,
            tuning_group=GroupNames.SCORING),
        'interaction_scoring':
        TunableList(
            description=
            '\n                Test for interactions run. Each test can have a separate score.\n                ',
            tunable=TunableTuple(
                description=
                '\n                    Each affordance that satisfies the test will receive the\n                    same score.\n                    ',
                score=Tunable(
                    description=
                    '\n                        Score for passing the test.\n                        ',
                    tunable_type=int,
                    default=1),
                affordance_list=event_testing.tests_with_data.
                TunableParticipantRanInteractionTest(
                    locked_args={
                        'participant': ParticipantType.Actor,
                        'tooltip': None,
                        'running_time': None
                    })),
            tuning_group=GroupNames.SCORING),
        'crafted_object_scoring':
        TunableList(
            description=
            '\n                Test for objects crafted. Each test can have a separate score.\n                ',
            tunable=TunableTuple(
                description=
                '\n                    Test for objects crafted. Each test can have a separate\n                    score.\n                    ',
                score=Tunable(
                    description=
                    '\n                        Score for passing the test.\n                        ',
                    tunable_type=int,
                    default=1),
                object_list=event_testing.test_variants.TunableCraftedItemTest(
                    description=
                    '\n                        A test to see if the crafted item should give score.\n                        ',
                    locked_args={'tooltip': None})),
            tuning_group=GroupNames.SCORING),
        'rewards':
        TunableMapping(
            description=
            '\n                Rewards given to the sim in this job when situation reaches specific medals.\n                ',
            key_type=TunableEnumEntry(
                SituationMedal,
                SituationMedal.TIN,
                description=
                '\n                    Medal to achieve to get the corresponding benefits.\n                    '
            ),
            value_type=SituationJobReward.TunableFactory(
                description=
                '\n                    Reward and LootAction benefits for accomplishing the medal.\n                    '
            ),
            key_name='medal',
            value_name='benefit',
            tuning_group=GroupNames.SCORING),
        'filter':
        TunableReference(manager=services.get_instance_manager(
            sims4.resources.Types.SIM_FILTER),
                         needs_tuning=True,
                         class_restrictions=TunableSimFilter),
        'tags':
        TunableSet(
            description=
            '\n                Designer tagging for making the game more fun.\n                ',
            tunable=TunableEnumEntry(tunable_type=Tag, default=Tag.INVALID)),
        'job_uniform':
        OptionalTunable(
            description=
            '\n                If enabled, when a Sim is assigned this situation job, that Sim\n                will switch into their outfit based on the Outfit Category.\n                \n                If the Outfit Category is SITUATION, then an outfit will be\n                generated based on the passed in tags and the Sim will switch\n                into that outfit.\n                ',
            tunable=TunableTuple(
                description='\n                    ',
                outfit_change_reason=TunableEnumEntry(
                    description=
                    '\n                        An enum that represents a reason for outfit change for\n                        the outfit system.\n                        \n                        An outfit change reason is really a series of tests\n                        that are run to determine which outfit category that\n                        we want to switch into.\n                        \n                        In order to do this, go to the tuning file\n                        sims.sim_outfits.\n                        \n                        Add a new OutfitChangeReason enum entry for your change\n                        reason.\n                        \n                        Then go into\n                        ClothingChangeTunables->Clothing Reasons To Outfits\n                        and add a new entry to the map.\n                        \n                        Set this entry to your new enum entry.\n                        \n                        Then you can add new elements to the list of tests and\n                        outfit categories that you want it to change the sim\n                        into.\n                        ',
                    tunable_type=OutfitChangeReason,
                    default=OutfitChangeReason.Invalid),
                outfit_change_priority=TunableEnumEntry(
                    description=
                    '\n                        The outfit change priority.  Higher priority outfit\n                        changes will override lower priority outfit changes.\n                        ',
                    tunable_type=DefaultOutfitPriority,
                    default=DefaultOutfitPriority.NoPriority),
                playable_sims_change_outfits=Tunable(
                    description=
                    '\n                        If checked, Playable Sims will change outfit when the job is set for the Sim. This\n                        should be checked on things like user facing events,\n                        but not Venue Background Event Jobs.\n                        ',
                    tunable_type=bool,
                    default=True),
                situation_outfit_generation_tags=OptionalTunable(
                    description=
                    "\n                        If enabled, the situation will use the outfit tags\n                        specified to generate an outfit for the sim's\n                        SITUATION outfit category.  If generating an outfit\n                        make sure to set outfit change reason to something that\n                        will put the sim into the SITUATION outfit category or\n                        you will not have the results that you expect.\n                        ",
                    tunable=TunableSet(
                        description=
                        '\n                            Only one of these sets is picked randomly to select the\n                            outfit tags within this set.\n                            E.g. If you want to host a costume party where the guests show\n                            up in either octopus costume or a shark costume, we would have\n                            two sets of tuning that can specify exclusive tags for the \n                            specific costumes. Thus we avoid accidentally generating a \n                            sharktopus costume.\n                            \n                            If you want your guests to always show up in sharktopus costumes \n                            then tune only one set of tags that enlist all the outfit tags\n                            that are associated with either shark or octopus.\n                            ',
                        tunable=TunableSet(
                            description=
                            "\n                                Tags that will be used by CAS to generate an outfit\n                                within the sim's SITUATION outfit category.\n                                ",
                            tunable=TunableEnumWithFilter(
                                tunable_type=Tag,
                                filter_prefixes=['uniform', 'outfitcategory'],
                                default=Tag.INVALID))))),
            disabled_name='no_uniform',
            enabled_name='uniform_specified'),
        'can_be_hired':
        Tunable(description=
                '\n                This job can be hired.\n                ',
                tunable_type=bool,
                default=True),
        'hire_cost':
        Tunable(
            description=
            '\n                The cost to hire a Sim for this job in Simoleons.\n                ',
            tunable_type=int,
            default=0),
        'game_breaker':
        Tunable(
            description=
            '\n                If True then this job must be filled by a sim\n                or the game will be broken. This is for the grim reaper and\n                the social worker.\n                ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES,
            tuning_filter=FilterTag.EXPERT_MODE),
        'elevated_importance':
        Tunable(
            description=
            '\n                If True, then filling this job with a Sim will be done before\n                filling similar jobs in this situation. This will matter when\n                starting a situation on another lot, when inviting a large number\n                of Sims, visiting commercial venues, or when at the cap on NPCs.\n                \n                Examples:\n                Wedding Situation: the Bethrothed Sims should be spawned before any guests.\n                Birthday Party: the Sims whose birthday it is should be spawned first.\n                Bar Venue: the Bartender should be spawned before the barflies.\n                \n                ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES,
            tuning_filter=FilterTag.EXPERT_MODE),
        'no_show_action':
        TunableEnumEntry(
            situations.situation_types.JobHolderNoShowAction,
            default=situations.situation_types.JobHolderNoShowAction.
            DO_NOTHING,
            description=
            "\n                                The action to take if no sim shows up to fill this job.\n                                \n                                Examples: \n                                If your usual maid doesn't show up, you want another one (REPLACE_THEM).\n                                If one of your party guests doesn't show up, you don't care (DO_NOTHING)\n                                If the President of the United States doesn't show up for the inauguration, you are hosed (END_SITUATION)\n                                ",
            tuning_group=GroupNames.SPECIAL_CASES),
        'died_or_left_action':
        TunableEnumEntry(
            situations.situation_types.JobHolderDiedOrLeftAction,
            default=situations.situation_types.JobHolderDiedOrLeftAction.
            DO_NOTHING,
            description=
            "\n                                    The action to take if a sim in this job dies or leaves the lot.\n                                    \n                                    Examples: \n                                    If the bartender leaves the ambient bar situation, you need a new one (REPLACE_THEM)\n                                    If your creepy uncle leaves the wedding, you don't care (DO_NOTHING)\n                                    If your maid dies cleaning the iron maiden, you are out of luck for today (END_SITUATION).\n                                    \n                                    NB: Do not use REPLACE_THEM if you are using Sim Churn for this job.\n                                    ",
            tuning_group=GroupNames.SPECIAL_CASES),
        'sim_spawner_tags':
        TunableList(
            description=
            '\n            A list of tags that represent where to spawn Sims for this Job when they come onto the lot.\n            NOTE: Tags will be searched in order of tuning. Tag [0] has priority over Tag [1] and so on.\n            ',
            tunable=TunableEnumEntry(tunable_type=Tag, default=Tag.INVALID)),
        'sim_spawn_action':
        TunableSpawnActionVariant(
            description=
            '\n            Define the methods to show the Sim after spawning on the lot.\n            '
        ),
        'sim_spawner_leave_option':
        TunableEnumEntry(
            description=
            '\n            The method for selecting spawn points for sims that are\n            leaving the lot. \n            \n            TUNED CONSTRAINT TAGS come from the SpawnPointConstraint.\n            SAVED TAGS are the same tags that were used to spawn the sim. \n            SAME vs DIFFERENT vs ANY resolves how to use the spawn point\n            that was saved when the sim entered the lot.\n            ',
            tunable_type=SpawnPointOption,
            default=SpawnPointOption.SPAWN_SAME_POINT),
        'emotional_setup':
        TunableList(
            description=
            '\n                Apply the WeightedSingleSimLootActions on the sim that is assigned this job. These are applied\n                only on NPC sims since the tuning is forcing changes to emotions.\n                \n                E.g. an angry mob at the bar, flirty guests at a wedding party.\n                ',
            tunable=TunableTuple(
                single_sim_loot_actions=WeightedSingleSimLootActions.
                TunableReference(),
                weight=Tunable(
                    int, 1, description='Accompanying weight of the loot.')),
            tuning_group=GroupNames.ON_CREATION),
        'commodities':
        TunableList(
            description=
            '\n                Update the commodities on the sim that is assigned this job. These are applied only on\n                NPC sims since the tuning is forcing changes to statistics that have player facing effects.\n             \n                E.g. The students arrive at the lecture hall with the bored and sleepy commodities.\n                ',
            tunable=TunableStatisticChange(
                locked_args={
                    'subject': ParticipantType.Actor,
                    'advertise': False,
                    'chance': 1,
                    'tests': None
                }),
            tuning_group=GroupNames.ON_CREATION),
        'requirement_text':
        TunableLocalizedString(
            description=
            '\n                A string that will be displayed in the sim picker for this\n                job in the situation window.\n                '
        ),
        'goodbye_notification':
        TunableVariant(
            description=
            '\n                The "goodbye" notification that will be set on Sims with this\n                situation job. This notification will be displayed when the\n                Sim leaves the lot (unless it gets overridden later).\n                Examples: the visitor job sets the "goodbye" notification to\n                something so the player knows when visitors leave; the party\n                guest roles use "No Notification", because we don\'t want 20-odd\n                notifications when a party ends; the leave lot jobs use "Use\n                Previous Notification" because we want leaving Sims to display\n                whatever notification was set earlier.\n                ',
            notification=TunableUiDialogNotificationSnippet(),
            locked_args={
                'no_notification':
                None,
                'never_use_notification_no_matter_what':
                SetGoodbyeNotificationElement.
                NEVER_USE_NOTIFICATION_NO_MATTER_WHAT,
                'use_previous_notification':
                DEFAULT
            },
            default='no_notification'),
        'additional_filter_for_user_selection':
        TunableReference(
            description=
            '\n                An additional filter that will run for the situation job if\n                there should be specific additional requirements for selecting\n                specific sims for the role rather than hiring them.\n                ',
            manager=services.get_instance_manager(
                sims4.resources.Types.SIM_FILTER),
            needs_tuning=True),
        'recommended_objects':
        TunableList(
            description=
            '\n                A list of objects that are recommended to be on a lot to get\n                the most out of this job\n                ',
            tunable=TunableVenueObject(
                description=
                "\n                        Specify object tag(s) that should be on this lot.\n                        Allows you to group objects, i.e. weight bench,\n                        treadmill, and basketball goals are tagged as\n                        'exercise objects.'\n                        "
            ),
            export_modes=ExportModes.All)
    }

    @classmethod
    def _verify_tuning_callback(cls):
        messages = []
        if cls.died_or_left_action == situations.situation_types.JobHolderDiedOrLeftAction.REPLACE_THEM:
            messages.append('Died Or Left Action == REPLACE_THEM')
        if cls.churn is not None:
            messages.append('Sim Churn')
        if cls.sim_shifts is not None:
            messages.append('Sim Shifts')
        if len(messages) > 1:
            message = ', and '.join(messages)
            logger.error('Situation job :{} must use only one of {}', cls,
                         message)

    @classmethod
    def get_score(cls, event, resolver, **kwargs):
        if event == event_testing.test_variants.TestEvent.InteractionComplete:
            for score_list in cls.interaction_scoring:
                while resolver(score_list.affordance_list):
                    return score_list.score
        elif event == event_testing.test_variants.TestEvent.ItemCrafted:
            for score_list in cls.crafted_object_scoring:
                while resolver(score_list.object_list):
                    return score_list.score
        return 0

    @classmethod
    def get_goal_score(cls):
        return cls.goal_scoring

    @classmethod
    def get_auto_invite(cls):
        if cls.churn is not None:
            interval = cls.churn.get_auto_populate_interval()
        else:
            if cls.sim_shifts is not None:
                return cls.sim_shifts.get_shift_staffing()
            if cls.sim_auto_invite.upper_bound > 0:
                interval = AutoPopulateInterval(
                    min=cls.sim_auto_invite.lower_bound,
                    max=cls.sim_auto_invite.upper_bound)
            else:
                return 0
        auto_invite = random.randrange(interval.min, interval.max + 1)
        return auto_invite

    @classmethod
    def can_sim_be_given_job(cls, sim_id, requesting_sim_info):
        if cls.filter is None:
            return True
        household_id = 0
        if requesting_sim_info is not None:
            household_id = requesting_sim_info.household.id
        return services.sim_filter_service().does_sim_match_filter(
            sim_id,
            sim_filter=cls.filter,
            requesting_sim_info=requesting_sim_info,
            household_id=household_id)
示例#24
0
        def start_situation(resolver,
                            situation,
                            user_facing=True,
                            invite_participants=None,
                            invite_actor=True,
                            actor_init_job=None,
                            invite_picked_sims=True,
                            invite_target_sim=True,
                            target_init_job=None,
                            invite_household_sims_on_active_lot=False,
                            situation_default_target=None,
                            situation_created_callback=None,
                            linked_sim_participant=None,
                            situation_guest_info=None,
                            **kwargs):
            situation_manager = services.get_zone_situation_manager()

            def create_guest_info(sim_id, job_type):
                if situation_guest_info is None:
                    return SituationGuestInfo.construct_from_purpose(
                        sim_id, job_type, SituationInvitationPurpose.INVITED)
                return situation_guest_info(sim_id, job_type)

            guest_list = situation.get_predefined_guest_list()
            if guest_list is None:
                sim = resolver.get_participant(ParticipantType.Actor)
                guest_list = SituationGuestList(invite_only=True,
                                                host_sim_id=sim.id)
                if situation.targeted_situation is not None:
                    target_sim = resolver.get_participant(
                        ParticipantType.PickedSim)
                    if target_sim is None:
                        target_sim = resolver.get_participant(
                            ParticipantType.TargetSim)
                    target_sim_id = target_sim.id if target_sim is not None else None
                    job_assignments = situation.get_prepopulated_job_for_sims(
                        sim, target_sim_id)
                    for (sim_id, job_type_id) in job_assignments:
                        job_type = services.situation_job_manager().get(
                            job_type_id)
                        guest_info = create_guest_info(sim_id, job_type)
                        guest_list.add_guest_info(guest_info)
                else:
                    default_job = situation.default_job()
                    if invite_picked_sims:
                        target_sims = resolver.get_participants(
                            ParticipantType.PickedSim)
                        roommate_service = services.get_roommate_service()
                        if roommate_service is not None:
                            target_sims = tuple(
                                itertools.chain(
                                    target_sims,
                                    roommate_service.get_auto_invite_sim_infos(
                                        sim, situation)))
                        if target_sims:
                            for sim_or_sim_info in target_sims:
                                guest_info = create_guest_info(
                                    sim_or_sim_info.sim_id, default_job)
                                guest_list.add_guest_info(guest_info)
                    if invite_target_sim:
                        target_sim = resolver.get_participant(
                            ParticipantType.TargetSim)
                        if target_sim is not None:
                            init_job = target_init_job if target_init_job is not None else default_job
                            guest_info = create_guest_info(
                                target_sim.sim_id, init_job)
                            guest_list.add_guest_info(guest_info)
                    if invite_actor and guest_list.get_guest_info_for_sim(
                            sim) is None:
                        init_job = actor_init_job if actor_init_job is not None else default_job
                        guest_info = create_guest_info(sim.sim_id, init_job)
                        guest_list.add_guest_info(guest_info)
                    if invite_household_sims_on_active_lot:
                        sims_to_invite = resolver.get_participants(
                            ParticipantType.ActiveHousehold)
                        if sims_to_invite:
                            for sim_info in sims_to_invite:
                                if guest_list.get_guest_info_for_sim(
                                        sim_info) is not None:
                                    continue
                                if sim_info.is_instanced(
                                        allow_hidden_flags=ALL_HIDDEN_REASONS):
                                    guest_info = create_guest_info(
                                        sim_info.sim_id, default_job)
                                    guest_list.add_guest_info(guest_info)
                    for (paticipant_type,
                         situation_jobs) in invite_participants.items():
                        target_sims = resolver.get_participants(
                            paticipant_type)
                        if target_sims:
                            jobs = situation_jobs if situation_jobs is not None else (
                                default_job, )
                            for (sim_or_sim_info,
                                 job_to_assign) in zip(target_sims,
                                                       itertools.cycle(jobs)):
                                if not sim_or_sim_info.is_sim:
                                    continue
                                guest_info = create_guest_info(
                                    sim_or_sim_info.sim_id, job_to_assign)
                                guest_list.add_guest_info(guest_info)
            zone_id = resolver.get_participant(
                ParticipantType.PickedZoneId) or 0
            default_target_id = None
            default_location = None
            if situation_default_target is not None:
                target_obj = resolver.get_participant(situation_default_target)
                if target_obj is not None:
                    default_target_id = target_obj.id
                    if target_obj.is_sim:
                        sim_instance = target_obj.get_sim_instance()
                        if sim_instance is not None:
                            default_location = sim_instance.location
                    else:
                        default_location = target_obj.location
            linked_sim_id = 0
            if linked_sim_participant is not None:
                linked_sim = resolver.get_participant(linked_sim_participant)
                if linked_sim is not None:
                    linked_sim_id = linked_sim.sim_id
            situation_id = situation_manager.create_situation(
                situation,
                guest_list=guest_list,
                user_facing=user_facing,
                zone_id=zone_id,
                default_target_id=default_target_id,
                default_location=default_location,
                linked_sim_id=linked_sim_id,
                **kwargs)
            if situation_id is None:
                return False
            if situation_created_callback is not None:
                situation_created_callback(situation_id)
            return True
示例#25
0
class Situation(BaseSituation, HasTunableReference, metaclass=HashedTunedInstanceMetaclass, manager=services.get_instance_manager(sims4.resources.Types.SITUATION)):

    @staticmethod
    def _verify_situation_level_tuning(instance_class, tunable_name, source, bronze, gold, silver, tin):
        gold_reward = gold.reward
        if gold_reward is not None and gold_reward.reward_description is None:
            logger.error('Situation "{}" has a Gold tier reward that has no Reward Description tuned. Bronze and Silver are optional, but Gold requires a description.', source, owner='asantos')

    INSTANCE_SUBCLASSES_ONLY = True
    NPC_HOSTED_SITUATION_AGE_WEIGHTING = TunableMapping(description='\n        A map of ages to weights when determining which sim in the household\n        will be selected to receive an invitation.\n        ', key_name='age', key_type=TunableEnumEntry(description='\n            The age of a possible invitee that will be mapped to a weight.\n            ', tunable_type=Age, default=Age.ADULT), value_name='weight', value_type=TunableRange(description='\n            The weight a sim of this age will be chosen to have an event run\n            on them.\n            ', tunable_type=int, default=1, minimum=1))
    INSTANCE_TUNABLES = {'category': TunableEnumEntry(description='\n                The Category that the Situation belongs to.\n                ', tunable_type=SituationCategoryUid, default=SituationCategoryUid.DEFAULT, tuning_group=GroupNames.UI), 'load_open_street_situation_with_selectable_sim': Tunable(description='\n            If the situation has selectable sims, set to True to ensure the\n            situation can load from the open street, False otherwise.\n            \n            Note: The Serialization Option also determines save/load strategy.\n            Check with GPE to verify the situation save/load behavior.\n            ', tunable_type=bool, default=False), '_display_name': TunableLocalizedString(description='\n                Display name for situation\n                ', allow_none=True, tuning_group=GroupNames.UI), 'situation_description': TunableLocalizedString(description='\n                Situation Description\n                ', allow_none=True, tuning_group=GroupNames.UI), 'entitlement': OptionalTunable(description='\n            If enabled, this situation is locked by an entitlement. Otherwise,\n            this situation is available to all players.\n            ', tunable=TunableEntitlement(description='\n                Entitlement required to plan this event.\n                ', tuning_group=GroupNames.UI)), '_default_job': TunableReference(description='\n                The default job for Sims in this situation\n                ', manager=services.situation_job_manager(), allow_none=True), '_resident_job': SituationJob.TunableReference(description='\n                The job to assign to members of the host sims household.\n                Make sure to use the in_family filter term in the filter\n                of the job you reference here.\n                It is okay if this tunable is None.\n                ', allow_none=True), '_icon': TunableResourceKey(description='\n                Icon to be displayed in the situation UI.\n                ', resource_types=sims4.resources.CompoundTypes.IMAGE, default=None, allow_none=True, tuning_group=GroupNames.UI), 'calendar_icon': TunableIconAllPacks(description='\n            Icon to be displayed in the calendar UI.\n            ', allow_none=True, tuning_group=GroupNames.UI), 'calendar_alert_description': OptionalTunable(description='\n            If tuned, there will be a calendar alert description.\n            ', tunable=TunableLocalizedString(description='\n                Description that shows up in the calendar alert.\n                ')), '_level_data': TunableTuple(tin=TunableSituationLevel(description='\n                    Tuning for the Tin level of this situation.  This level has\n                    a score delta of 0 as it is considered the default level\n                    of any situation.\n                    ', locked_args={'medal': SituationMedal.TIN, 'score_delta': 0}), bronze=TunableSituationLevel(description='\n                    Tuning for the Bronze level of this situation.\n                    ', locked_args={'medal': SituationMedal.BRONZE}), silver=TunableSituationLevel(description='\n                    Tuning for the Silver level of this situation.\n                    ', locked_args={'medal': SituationMedal.SILVER}), gold=TunableSituationLevel(description='\n                    Tuning for the Gold level of this situation.\n                    ', locked_args={'medal': SituationMedal.GOLD}), description='\n                    Tuning for the different situation levels and rewards that\n                    are associated with them.\n                    ', verify_tunable_callback=_verify_situation_level_tuning), 'job_display_ordering': OptionalTunable(description='\n            An optional list of the jobs in the order you want them displayed\n            in the Plan an Event UI.\n            ', tunable=TunableList(tunable=TunableReference(manager=services.situation_job_manager())), tuning_group=GroupNames.UI), 'recommended_job_object_notification': ui.ui_dialog_notification.UiDialogNotification.TunableFactory(description='\n            The notification that is displayed when one or more recommended objects\n            for a job are missing.\n            ', locked_args={'text': None}), 'recommended_job_object_text': TunableLocalizedStringFactory(description='\n            The text of the notification that is displayed when one or more recommended\n            objects for a job are missing.\n            \n            The localization tokens for the Text field are:\n            {0.String} = bulleted list of strings for the missing objects\n            ', allow_none=True), '_buff': TunableBuffReference(description='\n                Buff that will get added to sim when commodity is at this\n                current state.\n                ', allow_none=True), '_cost': Tunable(description='\n                The cost of this situation\n                ', tunable_type=int, default=0), 'exclusivity': TunableEnumEntry(description='\n            Defines the exclusivity category for the situation which is used to prevent sims assigned\n            to this situation from being assigned to situations from categories excluded by this\n            category and vice versa.\n            ', tunable_type=situations.bouncer.bouncer_types.BouncerExclusivityCategory, default=situations.bouncer.bouncer_types.BouncerExclusivityCategory.NORMAL), 'main_goal': TunableReference(description='The main goal of the situation. e.g. Get Married.', manager=services.get_instance_manager(sims4.resources.Types.SITUATION_GOAL), allow_none=True, tuning_group=GroupNames.GOALS), 'main_goal_audio_sting': TunableResourceKey(description='\n                The sound to play when the main goal of this situation\n                completes.\n                ', default=None, resource_types=(sims4.resources.Types.PROPX,), tuning_group=GroupNames.AUDIO), '_main_goal_visibility_test': OptionalTunable(description='\n                If enabled then the main goal of the situation will not be\n                visible until this test passes.  If the state of this test no\n                longer becomes true then the main gaol will not become\n                invisible again.\n                \n                Ex. A hospital emergency starting triggers the visiblity of the\n                main goal within the active career event situation.\n                \n                IMPORTANT: The nature of this test can cause performance\n                problems.\n                ', tunable=TunableMainGoalVisibilityTestVariant(), tuning_group=GroupNames.GOALS), 'minor_goal_chains': TunableList(description='\n                A list of goal sets, each one starting a chain of goal sets, for selecting minor goals.\n                The list is in priority order, first being the most important.\n                At most one goal will be selected from each chain.\n                ', tunable=situations.situation_goal_set.SituationGoalSet.TunableReference(), tuning_group=GroupNames.GOALS), 'force_invite_only': Tunable(description='\n                If True, the situation is invite only. Otherwise, it is not.\n                For a date situation, this would be set to True.\n                ', tunable_type=bool, default=False), 'creation_ui_option': TunableEnumEntry(description='\n                Determines if the situation can be created from the Plan Event\n                UI triggered from the phone.\n                \n                NOT_AVAILABLE - situation is not available in the creation UI.\n                \n                AVAILABLE - situation is available in the creation UI.\n                \n                DEBUG_AVAILABLE - situation is only available in the UI if\n                you have used the |situations.allow_debug_situations command\n                \n                SPECIFIED_ONLY - situation is available in the creation UI if\n                that UI is tuned to only look at a subset of situations.\n                ', tunable_type=SituationCreationUIOption, default=SituationCreationUIOption.AVAILABLE, tuning_group=GroupNames.UI), 'audio_sting_on_start': TunableResourceKey(description='\n                The sound to play when the Situation starts.\n                ', default=None, resource_types=(sims4.resources.Types.PROPX,), tuning_group=GroupNames.AUDIO), 'background_audio': OptionalTunable(description='\n                If enabled then we will play audio in the background while this\n                user facing situation is running.\n                ', tunable=TunableResourceKey(description='\n                    Audio that will play throughout the situation in the background\n                    and will end at the end of the situation.\n                    ', default=None, resource_types=(sims4.resources.Types.PROPX,)), tuning_group=GroupNames.AUDIO), 'duration': TunableSimMinute(description='\n                How long the situation will last in sim minutes. 0 means forever.\n                ', default=60), 'duration_randomizer': TunableSimMinute(description="\n            A random time between 0 and this tuned time will be added to the\n            situation's duration.\n            ", default=0, minimum=0), 'max_participants': Tunable(description='\n                Maximum number of Sims the player is allowed to invite to this Situation.\n                ', tunable_type=int, default=16, tuning_group=GroupNames.UI), '_initiating_sim_tests': TunableSituationInitiationSet('\n            A set of tests that will be run on a sim attempting to initiate a\n            situation.  If these tests do not pass than this situation will not\n            be able to be chosen from the UI.\n            '), 'targeted_situation': OptionalTunable(description='\n                If enabled, the situation can be used as a targeted situation,\n                such as a Date.\n                ', tunable=TargetedSituationSpecific.TunableFactory()), 'compatible_venues': TunableList(description='\n                In the Plan an Event UI, lots that are these venues will be\n                added to the list of lots on which the player can throw the\n                event. The player can always choose their own lot and lots of\n                their guests.\n                ', tunable=TunableReference(manager=services.get_instance_manager(sims4.resources.Types.VENUE), pack_safe=True, tuning_group=GroupNames.VENUES)), 'venue_region_must_be_compatible': Tunable(description='\n                If enabled, venues will only be considered if they are in a\n                region that is compatible with the current region (regions with\n                at least one shared tag).\n                ', tunable_type=bool, default=False), 'venue_invitation_message': OptionalTunable(description='\n            If enabled, show a dialog when the situation tries to start on a\n            venue.\n            ', tunable=UiDialogOkCancel.TunableFactory(description="\n                The message that will be displayed when this situation tries to\n                start for the venue.\n                \n                Two additional tokens are passed in: the situation's name and\n                the job's name.\n                "), tuning_group=GroupNames.VENUES), 'venue_situation_player_job': TunableReference(description="\n                The job that the player will be put into when they join in a\n                user_facing special situation at a venue.\n                \n                Note: This must be tuned to allow this situation to be in a\n                venue's special event schedule. The job also must be a part of\n                the Situation.\n                ", manager=services.get_instance_manager(sims4.resources.Types.SITUATION_JOB), allow_none=True, tuning_group=GroupNames.VENUES), 'tags': TunableSet(description='\n                Tags for arbitrary groupings of situation types.\n                ', tunable=TunableEnumWithFilter(tunable_type=Tag, filter_prefixes=['situation'], default=Tag.INVALID, pack_safe=True)), '_relationship_between_job_members': TunableList(description="\n                Whenever a sim joins either job_x or job_y, the sim is granted \n                the tuned relationship bit with every sim in the other job. The\n                relationship bits are added and remain as long as the sims are\n                assigned to the tuned pair of jobs.\n                \n                This creates a relationship between the two sims if one does not exist.\n                \n                E.g. Date situation uses this feature to add bits to the sims'\n                relationship in order to drive autonomous behavior during the \n                lifetime of the date. \n                ", tunable=TunableTuple(job_x=SituationJob.TunableReference(), job_y=SituationJob.TunableReference(), relationship_bits_to_add=TunableSet(description='\n                        A set of RelationshipBits to add to relationship between the sims.\n                        ', tunable=RelationshipBit.TunableReference())), tuning_group=GroupNames.TRIGGERS), '_implies_greeted_status': Tunable(description='\n                If checked then a sim, in this situation, on a residential lot\n                they do not own, is consider greeted on that lot.\n                \n                Greeted status related design documents:\n                //depot/Sims4Projects/docs/Design/Gameplay/HouseholdState/Ungreeted_Lot_Behavior_DD.docx\n                //depot/Sims4Projects/docs/Design/Gameplay/Simulation/Active Lot Changing Edge Cases.docx\n                ', tunable_type=bool, default=False), 'screen_slam_no_medal': OptionalTunable(description='\n            Screen slam to show when this situation is completed and no\n            medal is earned.\n            Localization Tokens: Event Name - {0.String}, Medal Awarded - \n            {1.String}\n            ', tunable=ui.screen_slam.TunableScreenSlamSnippet()), 'screen_slam_bronze': OptionalTunable(description='\n            Screen slam to show when this situation is completed and a\n            bronze medal is earned.\n            Localization Tokens: Event Name - {0.String}, Medal Awarded - \n            {1.String}\n            ', tunable=ui.screen_slam.TunableScreenSlamSnippet()), 'screen_slam_silver': OptionalTunable(description='\n            Screen slam to show when this situation is completed and a\n            silver medal is earned.\n            Localization Tokens: Event Name - {0.String}, Medal Awarded - \n            {1.String}\n            ', tunable=ui.screen_slam.TunableScreenSlamSnippet()), 'screen_slam_gold': OptionalTunable(description='\n            Screen slam to show when this situation is completed and a\n            bronze medal is earned.\n            Localization Tokens: Event Name - {0.String}, Medal Awarded - \n            {1.String}\n            ', tunable=ui.screen_slam.TunableScreenSlamSnippet()), 'time_jump': TunableSituationTimeJumpVariant(description='\n            Determine how the situation handles the zone time being different on\n            load than what it was on save. This is primarily useful for\n            commercial venue background situations and career event situations.\n            ', tuning_group=GroupNames.SPECIAL_CASES), 'can_remove_sims_from_work': Tunable(description='\n            If checked then this situation will cause sims to end work early\n            when they are on the guest list. If unchecked, it will not. This\n            option will not affect active career situations or NPC career\n            situations like tending the bar.\n            ', tunable_type=bool, default=True, tuning_group=GroupNames.SPECIAL_CASES), '_survives_active_household_change': Tunable(description='\n            If checked then this situation will load even if the active\n            household has changed since it was saved. It will attempt to\n            restore Sims to their saved jobs. This is primarily useful for\n            commercial venue background situations.\n            ', tunable_type=bool, default=False, tuning_group=GroupNames.SPECIAL_CASES), '_maintain_sims_consistency': Tunable(description="\n            If checked, Sims in the saved situation that were pushed home \n            because they had been saved in the zone for many Sim hours will \n            be back. Otherwise, we will find replacement.\n            \n            Ex. We don't want to replace Butler with new Sim if previous\n            Butler is no longer in the lot.\n            ", tunable_type=bool, default=False, tuning_group=GroupNames.SPECIAL_CASES), '_hidden_scoring_override': Tunable(description='\n            If checked then even if this situation has its scoring disabled it\n            still will count score and provide rewards to the player.\n            ', tunable_type=bool, default=False, tuning_group=GroupNames.SPECIAL_CASES), '_ensemble': OptionalTunable(description='\n            If enabled then we will keep Sims in a specific ensemble for the\n            duration of the situation.\n            ', tunable=TunableTuple(description='\n                Tunables for putting Sims into ensembles.\n                ', ensemble_type=TunableReference(description='\n                    The type of Ensemble to put the sims into.\n                    ', manager=services.get_instance_manager(sims4.resources.Types.ENSEMBLE), pack_safe=True), remove_before_add=Tunable(description='\n                    If checked then before we add the Sim to the ensemble we\n                    will remove them from from ensembles of the specified type.\n                    This can be used to force Sims into only an ensemble of\n                    Sims in this situation.\n                    ', tunable_type=bool, default=False), ignore_situation_removal=Tunable(description='\n                    If checked then we will not remove the Sim from the\n                    ensemble of this type when the Sim is removed from the\n                    situation.\n                    ', tunable_type=bool, default=True), ensemble_option=TunableEnumEntry(description='\n                    How we want to add Sims to an ensemble:\n                    ONLY_WITHIN_SITUATION: Put the Sims in this situation into\n                    an ensemble of this type.  Every time a sim is added we\n                    try and do this so if the user destroys the ensemble and\n                    then another Sim is spawned for it the ensemble will be\n                    recreated.\n                    \n                    ADD_TO_ACTIVE_HOUSEHOLD: Every time a Sim is spawned for\n                    this situation they are put into an ensemble with the\n                    instanced active household.  This is useful if you want to\n                    put the Sims in a situation with someone who is not in it. \n                    \n                    ADD_TO_HOST: Every time a Sim is spawned for this situation\n                    they are put into an ensemble with the host of the\n                    situation.  This is useful if you want to put the Sims in\n                    a situation with someone who is not in it.\n                    ', tunable_type=EnsembleOption, default=EnsembleOption.ONLY_WITHIN_SITUATION)), tuning_group=GroupNames.SPECIAL_CASES), 'blocks_super_speed_three': Tunable(description='\n            If enabled, this situation will block any requests to go into super\n            speed 3.\n            ', tunable_type=bool, default=False), 'travel_request_behavior': TunableSituationTravelRequestBehaviorVariant(description='\n            Define how this situation handles incoming travel requests from\n            other situations when running as user-facing.\n            '), 'allowed_in_super_speed_3': Tunable(description="\n            If enabled, this situation will skip the super speed 3 rules and\n            be allowed to trigger at that speed.\n            This will only affect walkby's as they are the only restricted\n            by speed 3.\n            ", tunable_type=bool, default=False), 'should_send_on_lot_home_in_super_speed_3': Tunable(description='\n            If enabled, on_lot sims in this situation will not prevent SS3.  If\n            SS3 is triggered they will be sent home.\n            ', tunable_type=bool, default=False), 'super_speed3_replacement_speed': OptionalTunable(description='\n            If enabled and this situation blocks super speed 3, the situation will attempt to request\n            this speed if it is running when super speed 3 tries to kick in.\n            ', tunable=TunableEnumEntry(tunable_type=ClockSpeedMode, invalid_enums=(ClockSpeedMode.PAUSED, ClockSpeedMode.INTERACTION_STARTUP_SPEED, ClockSpeedMode.SUPER_SPEED3), default=ClockSpeedMode.SPEED3)), 'weight_multipliers': TunableMultiplier.TunableFactory(description="\n            Tunable tested multiplier to apply to any weight this situation\n            might have as part of a Situation Curve. These multipliers will be\n            applied globally anywhere this situation is tuned as part of a\n            situation curve (i.e. Walkby Tuning) so it should only be used in\n            cases where you ALWAYS want this multiplier applied.\n            \n            NOTE: You can still tune more multipliers on the individual walk by\n            instances. The multipliers will all stack together.\n            \n            *IMPORTANT* The only participants that work are ones\n            available globally, such as Lot and ActiveHousehold. Only\n            use these participant types or use tests that don't rely\n            on any, such as testing all objects via Object Criteria\n            test or testing active zone with the Zone test.\n            ", locked_args={'base_value': 1}), 'disallows_curfew_violation': Tunable(description='\n            If this is checked then the Sim is unable to violate curfew while\n            in the situation. If this is not checked then the Sim can vioalte\n            curfew as normal.\n            ', tunable_type=bool, default=False), 'suppress_scoring_progress_bar': Tunable(description='\n            If this is checked, UI will no longer show the scoring progress bar\n            and instead show the situation name in its stead.\n            ', tunable_type=bool, default=False, tuning_group=GroupNames.UI), 'highlight_first_incomplete_minor_goal': Tunable(description='\n            If this is checked, we will tell user-facing Situation UI \n            to highlight the first uncompleted minor goal set.\n            \n            Note that gameplay currently does not guard against being able \n            to complete the other goal sets in the situation currently, \n            so the goalsets should be tuned in such a manner \n            that they do not overlap.\n            ', tunable_type=bool, default=False, tuning_group=GroupNames.UI), '_use_spawner_tags_on_travel': Tunable(description='\n            If checked the situation will spawn sims according to its job spawner tags\n            instead of always defaulting to the Arrival spawner.\n            ', tunable_type=bool, default=False)}
    SITUATION_SCORING_REMOVE_INSTANCE_TUNABLES = ('main_goal', '_main_goal_visibility_test', 'minor_goal_chains', 'main_goal_audio_sting', 'highlight_first_incomplete_minor_goal', 'suppress_scoring_progress_bar', '_level_data', 'screen_slam_gold', 'screen_slam_silver', 'screen_slam_bronze', 'screen_slam_no_medal')
    SITUATION_START_FROM_UI_REMOVE_INSTANCE_TUNABLES = ('_cost', 'compatible_venues', 'venue_invitation_message', 'venue_situation_player_job', 'category', 'max_participants', '_initiating_sim_tests', '_icon', 'entitlement', 'job_display_ordering')
    SITUATION_USER_FACING_REMOVE_INSTANCE_TUNABLES = ('_display_name', 'travel_request_behavior', 'recommended_job_object_notification', 'recommended_job_object_text', 'situation_description')
    NON_USER_FACING_REMOVE_INSTANCE_TUNABLES = ('_buff', 'targeted_situation', '_resident_job', '_relationship_between_job_members', 'audio_sting_on_start', 'background_audio', 'force_invite_only') + SITUATION_SCORING_REMOVE_INSTANCE_TUNABLES + SITUATION_START_FROM_UI_REMOVE_INSTANCE_TUNABLES + SITUATION_USER_FACING_REMOVE_INSTANCE_TUNABLES
    SITUATION_EVENT_REMOVE_INSTANCE_TUNABLES = ('_buff', '_cost', 'venue_invitation_message', 'venue_situation_player_job', 'category', 'main_goal', '_main_goal_visibility_test', 'minor_goal_chains', 'highlight_first_incomplete_minor_goal', 'suppress_scoring_progress_bar', 'max_participants', '_initiating_sim_tests', '_icon', 'targeted_situation', '_resident_job', 'situation_description', 'job_display_ordering', 'entitlement', '_relationship_between_job_members', 'main_goal_audio_sting', 'audio_sting_on_start', 'background_audio', '_level_data', '_display_name', 'screen_slam_gold', 'screen_slam_silver', 'screen_slam_bronze', 'screen_slam_no_medal', 'force_invite_only', 'recommended_job_object_notification', 'recommended_job_object_text', 'travel_request_behavior')
    situation_level_data = None
    SituationLevel = collections.namedtuple('SituationLevel', ['min_score_threshold', 'level_data'])

    @classmethod
    def _tuning_loaded_callback(cls):
        cls.situation_level_data = []
        current_score = cls._level_data.tin.score_delta
        cls.situation_level_data.append(Situation.SituationLevel(current_score, cls._level_data.tin))
        current_score += cls._level_data.bronze.score_delta
        cls.situation_level_data.append(Situation.SituationLevel(current_score, cls._level_data.bronze))
        current_score += cls._level_data.silver.score_delta
        cls.situation_level_data.append(Situation.SituationLevel(current_score, cls._level_data.silver))
        current_score += cls._level_data.gold.score_delta
        cls.situation_level_data.append(Situation.SituationLevel(current_score, cls._level_data.gold))

    @classmethod
    def _verify_tuning_callback(cls):
        if cls._resident_job is not None and cls._resident_job.filter is None:
            logger.error('Resident Job: {} has no filter,', cls._resident_job, owner='manus')
        if cls.targeted_situation is not None and (cls.targeted_situation.target_job is None or cls.targeted_situation.actor_job is None):
            logger.error('target_job and actor_job are required if targeted_situation is enabled.', owner='manus')
        tuned_jobs = frozenset(cls.get_tuned_jobs())
        for job_relationships in cls.relationship_between_job_members:
            if job_relationships.job_x not in tuned_jobs:
                logger.error('job_x: {} has relationship tuning but is not functionally used in situation {}.', job_relationships.job_x, cls, owner='manus')
            if job_relationships.job_y not in tuned_jobs:
                logger.error('job_y: {} has relationship tuning but is not functionally used in situation {}.', job_relationships.job_y, cls, owner='manus')
            if len(job_relationships.relationship_bits_to_add) == 0:
                logger.error("relationship_bits_to_add cannot be empty for situation {}'s job pairs {} and {}.", cls, job_relationships.job_x, job_relationships.job_y, owner='manus')
            else:
                for bit in job_relationships.relationship_bits_to_add:
                    if bit is None:
                        logger.error("relationship_bits_to_add cannot contain empty bit for situation {}'s job pairs {} and {}.", cls, job_relationships.job_x, job_relationships.job_y, owner='manus')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._duration_alarm_handle = None
        self._goal_tracker = None
        self._dynamic_goals = self._seed.extra_kwargs.get('dynamic_goals', None)

    @classproperty
    def allow_user_facing_goals(cls):
        return cls.main_goal is not None or len(cls.minor_goal_chains) > 0

    @classmethod
    def level_data_gen(cls):
        for level in cls.situation_level_data:
            yield level

    @classmethod
    def fake_perform_job(cls):
        pass

    @classmethod
    def get_level_data(cls, medal:SituationMedal=SituationMedal.TIN):
        if cls.situation_level_data is None:
            return
        return cls.situation_level_data[medal].level_data

    @classmethod
    def get_level_min_threshold(cls, medal:SituationMedal=SituationMedal.TIN):
        if cls.situation_level_data is None:
            return
        return cls.situation_level_data[medal].min_score_threshold

    @classmethod
    def get_level_icon(cls, medal:SituationMedal=SituationMedal.TIN):
        if cls.situation_level_data is None:
            return
        return cls.situation_level_data[medal].level_data.icon

    @classmethod
    def get_possible_zone_ids_for_situation(cls, host_sim_info=None, guest_ids=None):
        possible_zones = []
        venue_manager = services.get_instance_manager(sims4.resources.Types.VENUE)
        venue_service = services.current_zone().venue_service
        for venue_tuning in cls.compatible_venues:
            if venue_tuning.is_residential:
                if host_sim_info is not None:
                    home_zone_id = host_sim_info.household.home_zone_id
                    home_venue_tuning = venue_manager.get(build_buy.get_current_venue(home_zone_id))
                    if home_venue_tuning.is_residential:
                        possible_zones.append(home_zone_id)
                if guest_ids is not None:
                    for guest_id in guest_ids:
                        guest_id = int(guest_id)
                        guest_info = services.sim_info_manager().get(guest_id)
                        if guest_info is not None:
                            guest_zone_id = guest_info.household.home_zone_id
                            if guest_zone_id is not None and guest_zone_id and guest_zone_id not in possible_zones:
                                guest_venue_tuning = venue_manager.get(build_buy.get_current_venue(guest_zone_id))
                                if guest_venue_tuning.is_residential:
                                    possible_zones.append(guest_zone_id)
                            travel_group = guest_info.travel_group
                            if travel_group is not None:
                                travel_group_zone_id = travel_group.zone_id
                                if travel_group_zone_id is not None and travel_group_zone_id and travel_group_zone_id not in possible_zones:
                                    possible_zones.append(travel_group_zone_id)
                    else:
                        possible_zones.extend(venue_service.get_zones_for_venue_type_gen(venue_tuning))
            else:
                possible_zones.extend(venue_service.get_zones_for_venue_type_gen(venue_tuning))
        return possible_zones

    @classmethod
    def default_job(cls):
        return cls._default_job

    @classmethod
    def resident_job(cls):
        return cls._resident_job

    @classmethod
    def get_prepopulated_job_for_sims(cls, sim, target_sim_id=None):
        if target_sim_id and cls.targeted_situation is not None:
            sim_info = services.sim_info_manager().get(target_sim_id)
            if sim_info is None:
                return
            else:
                prepopulated = [(sim.id, cls.targeted_situation.actor_job.guid64), (target_sim_id, cls.targeted_situation.target_job.guid64)]
                return prepopulated

    def _display_role_objects_notification(self, sim, bullets):
        text = self.recommended_job_object_text(bullets)
        notification = self.recommended_job_object_notification(sim, text=lambda *_, **__: text)
        notification.show_dialog()

    @property
    def pie_menu_icon(self):
        return self._pie_menu_icon

    @classproperty
    def display_name(self):
        return self._display_name

    @property
    def description(self):
        return self.situation_description

    @classproperty
    def icon(self):
        return self._icon

    @property
    def start_audio_sting(self):
        return self.audio_sting_on_start

    @property
    def audio_background(self):
        return self.background_audio

    def get_target_object(self):
        pass

    def get_created_object(self):
        pass

    @property
    def end_audio_sting(self):
        current_level = self.get_level()
        level_data = self.get_level_data(current_level)
        if level_data is not None and level_data.audio_sting_on_end is not None:
            return level_data.audio_sting_on_end
        else:
            return

    @classproperty
    def relationship_between_job_members(cls):
        return cls._relationship_between_job_members

    @classproperty
    def implies_greeted_status(cls):
        return cls._implies_greeted_status

    @classmethod
    def cost(cls):
        return cls._cost

    @classproperty
    def survives_active_household_change(cls):
        return cls._survives_active_household_change

    @classproperty
    def maintain_sims_consistency(cls):
        return cls._maintain_sims_consistency

    def _get_duration(self):
        if self._seed.duration_override is not None:
            return self._seed.duration_override
        return self.duration + random.randint(0, self.duration_randomizer)

    def _get_remaining_time(self):
        if self._duration_alarm_handle is None:
            return
        return self._duration_alarm_handle.get_remaining_time()

    def _get_remaining_time_for_gsi(self):
        return self._get_remaining_time()

    def _get_remaining_time_in_minutes(self):
        time_span = self._get_remaining_time()
        if time_span is None:
            return 0
        return time_span.in_minutes()

    def _get_goal_tracker(self):
        return self._goal_tracker

    def _save_custom(self, seed):
        super()._save_custom(seed)
        if self._goal_tracker is not None:
            self._goal_tracker.save_to_seed(seed)

    def start_situation(self):
        super().start_situation()
        self._set_duration_alarm()
        if self.is_user_facing:
            if self.should_track_score:
                if self._dynamic_goals is None:
                    self._goal_tracker = situations.situation_goal_tracker.SituationGoalTracker(self)
                else:
                    self._goal_tracker = situations.dynamic_situation_goal_tracker.DynamicSituationGoalTracker(self)

    def _load_situation_states_and_phases(self):
        super()._load_situation_states_and_phases()
        self._set_duration_alarm()
        if not self._seed.goal_tracker_seedling:
            return
        if self._seed.goal_tracker_seedling.goal_tracker_type == GoalTrackerType.STANDARD_GOAL_TRACKER:
            self._goal_tracker = situations.situation_goal_tracker.SituationGoalTracker(self)
        elif self._seed.goal_tracker_seedling.goal_tracker_type == GoalTrackerType.DYNAMIC_GOAL_TRACKER:
            self._goal_tracker = situations.dynamic_situation_goal_tracker.DynamicSituationGoalTracker(self)

    def change_duration(self, duration):
        if not self.is_running:
            logger.error("Trying to change the duration of a situation {} that's not running.", self)
        self._set_duration_alarm(duration_override=duration)
        if self.is_user_facing:
            self.add_situation_duration_change_op()

    def _set_duration_alarm(self, duration_override=None):
        if duration_override is not None:
            duration = duration_override
        else:
            duration = self._get_duration()
        self.set_end_time(duration)
        if duration > 0:
            if self._duration_alarm_handle is not None:
                alarms.cancel_alarm(self._duration_alarm_handle)
            self._duration_alarm_handle = alarms.add_alarm(self, clock.interval_in_sim_minutes(duration), self._situation_timed_out)

    def _cancel_duration_alarm(self):
        if self.is_user_facing:
            logger.error('Canceling duration alarm for a User-Facing Situation {}', self, owner='rmccord')
        if self._duration_alarm_handle is not None:
            alarms.cancel_alarm(self._duration_alarm_handle)

    def pre_destroy(self):
        pass

    def _destroy(self):
        if self._duration_alarm_handle is not None:
            alarms.cancel_alarm(self._duration_alarm_handle)
        if self._goal_tracker is not None:
            self._goal_tracker.destroy()
            self._goal_tracker = None
        super()._destroy()

    def _situation_timed_out(self, _):
        logger.debug('Situation time expired: {}', self)
        self._self_destruct()

    @classmethod
    def is_situation_available(cls, initiating_sim, target_sim_id=0):
        is_targeted = cls.targeted_situation is not None and cls.targeted_situation.target_job is not None
        if is_targeted and target_sim_id:
            if not cls.targeted_situation.target_job.can_sim_be_given_job(target_sim_id, initiating_sim.sim_info):
                return TestResult(False)
        elif (target_sim_id == 0) != (is_targeted == False):
            return TestResult(False)
        single_sim_resolver = event_testing.resolver.SingleSimResolver(initiating_sim.sim_info)
        return cls._initiating_sim_tests.run_tests(single_sim_resolver)

    @classmethod
    def get_predefined_guest_list(cls):
        pass

    @classmethod
    def is_venue_location_valid(cls, zone_id):
        compatible_region = services.current_region() if cls.venue_region_must_be_compatible else None
        return services.current_zone().venue_service.is_zone_valid_for_venue_type(zone_id, cls.compatible_venues, compatible_region=compatible_region)

    @classmethod
    def get_venue_location(cls):
        compatible_region = services.current_region() if cls.venue_region_must_be_compatible else None
        (zone_id, _) = services.current_zone().venue_service.get_zone_and_venue_type_for_venue_types(cls.compatible_venues, compatible_region=compatible_region)
        return zone_id

    @classmethod
    def has_venue_location(cls):
        compatible_region = services.current_region() if cls.venue_region_must_be_compatible else None
        return services.current_zone().venue_service.has_zone_for_venue_type(cls.compatible_venues, compatible_region=compatible_region)

    @classproperty
    def main_goal_visibility_test(cls):
        return cls._main_goal_visibility_test

    @classproperty
    def _ensemble_data(cls):
        return cls._ensemble

    @property
    def should_track_score(self):
        return self.scoring_enabled or self._hidden_scoring_override

    @property
    def should_give_rewards(self):
        return self.scoring_enabled or self._hidden_scoring_override

    def is_in_joinable_state(self):
        return True

    @property
    def custom_event_keys(self):
        return [type(self)] + list(self.tags)

    @classproperty
    def use_spawner_tags_on_travel(cls):
        return cls._use_spawner_tags_on_travel
示例#26
0
 def __init__(self, **kwargs):
     super().__init__(
         situation=TunableReference(
             description=
             '\n                The Situation to start when this Interaction runs.\n                ',
             manager=services.situation_manager()),
         user_facing=Tunable(
             description=
             '\n                If checked, then the situation will be user facing (have goals, \n                and scoring).\n                \n                If not checked, then situation will not be user facing.\n                \n                This setting does not override the user option to make all\n                situations non-scoring.\n                \n                Example: \n                    Date -> Checked\n                    Invite To -> Not Checked\n                ',
             tunable_type=bool,
             default=True),
         linked_sim_participant=OptionalTunable(
             description=
             '\n                If enabled, this situation will be linked to the specified Sim.\n                ',
             tunable=TunableEnumEntry(tunable_type=ParticipantType,
                                      default=ParticipantType.Actor)),
         invite_participants=TunableMapping(
             description=
             "\n                The map to invite certain participants into the situation as\n                specified job if assigned. Otherwise will invite them as\n                situation's default job.\n                ",
             key_type=TunableEnumEntry(
                 description=
                 '\n                    The participant of who will join the situation.\n                    ',
                 tunable_type=ParticipantType,
                 default=ParticipantType.Actor),
             key_name='participants_to_invite',
             value_type=OptionalTunable(tunable=TunableList(
                 description=
                 '\n                        A list of situation jobs that can be specified.  If a\n                        single job is specified then all Sims will be given\n                        that job.  Otherwise we will loop through all of the\n                        Sims invited and give them jobs in list order.  The\n                        list will begin to be repeated if we run out of jobs.\n                        \n                        NOTE: We cannot guarantee the order of the Sims being\n                        passed in most of the time.  Use this if you want a\n                        distribution of different jobs, but without a guarantee\n                        that Sims will be assigned to each one.\n                        ',
                 tunable=TunableReference(
                     manager=services.situation_job_manager())),
                                        disabled_name='use_default_job',
                                        enabled_name='specify_job'),
             value_name='invite_to_job'),
         invite_actor=Tunable(
             description=
             '\n                If checked, then the actor of this interaction will be invited\n                in the default job. This is the common case.\n                \n                If not checked, then the actor will not be invited. The Tell\n                A Ghost Story interaction spawning a Ghost walkby is an example.\n                \n                If your situation takes care of all the sims that should be in\n                the default job itself (such as auto-invite) it will probably\n                not work if this is checked.\n                ',
             tunable_type=bool,
             default=True),
         actor_init_job=OptionalTunable(
             description=
             '\n                The Situation job actor would be assigned while join the situation.\n                ',
             tunable=TunableReference(
                 manager=services.situation_job_manager()),
             disabled_name='use_default_job',
             enabled_name='specify_job'),
         invite_picked_sims=Tunable(
             description=
             '\n                If checked then any picked sims of this interaction will be\n                invited to the default job.  This is the common case.\n                \n                If not checked, then any picked sims will not be invited.  The\n                Tell A Ghost Story interaction spawning a Ghost walkby is an\n                example.\n                \n                If your situation takes care of all the sims that should be in\n                the default job itself (such as auto-invite) it will probably\n                not work if this is checked.\n                ',
             tunable_type=bool,
             default=True),
         invite_target_sim=Tunable(
             description=
             '\n                If checked then the target sim of this interaction will be\n                invited to the default job.  This is the common case.\n                \n                If not checked, then the target sim will not be invited.  The\n                Tell A Ghost Story interaction spawning a Ghost walkby is an\n                example.\n                \n                If your situation takes care of all the sims that should be in\n                the default job itself (such as auto-invite) it will probably\n                not work if this is checked.\n                ',
             tunable_type=bool,
             default=True),
         target_init_job=OptionalTunable(
             description=
             '\n                The Situation job target would be assigned while join the situation.\n                ',
             tunable=TunableReference(
                 manager=services.situation_job_manager()),
             disabled_name='use_default_job',
             enabled_name='specify_job'),
         invite_household_sims_on_active_lot=Tunable(
             description=
             '\n                If checked then all instanced sims on the active lot will be\n                invited. This is not a common case. An example of this is\n                leaving the hospital after having a baby, bringing both sims\n                home.\n                \n                If not checked, then no additional sims will be invited.\n                \n                If your situation takes care of all the sims that should be in\n                the default job itself (such as auto-invite) it will probably\n                not work if this is checked.\n                ',
             tunable_type=bool,
             default=False),
         situation_default_target=OptionalTunable(
             description=
             '\n                If enabled, the participant of the interaction will be set as\n                the situation target object.\n                ',
             tunable=TunableEnumEntry(
                 description=
                 "\n                    The participant that will be set as the situation's default target\n                    ",
                 tunable_type=ParticipantType,
                 default=ParticipantType.Object)),
         situation_guest_info=OptionalTunable(
             description=
             '\n                By default, situation guest infos are created as an invite.\n                This overrrides that behavior.\n                ',
             tunable=SituationGuestInfoFactory()),
         description='Start a Situation as part of this Interaction.',
         **kwargs)
示例#27
0
class IslandPreWelcomeWagon(PreWelcomeWagon):
    INSTANCE_TUNABLES = {'_lei_carrier_situation_job': TunableReference(description='\n            The job for the lei carrier.\n            ', manager=services.situation_job_manager())}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classproperty
    def sets_welcome_wagon_flag(cls):
        return True

    @classmethod
    def get_predefined_guest_list(cls):
        active_sim_info = services.active_sim_info()
        door_knocker_results = services.sim_filter_service().submit_filter(cls._door_knocker_situation_job.filter, callback=None, requesting_sim_info=active_sim_info, allow_yielding=False, gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not door_knocker_results:
            return
        door_knocker = random.choice(door_knocker_results)
        guest_list = SituationGuestList(invite_only=True, host_sim_id=door_knocker.sim_info.sim_id, filter_requesting_sim_id=active_sim_info.sim_id)
        guest_list.add_guest_info(SituationGuestInfo(door_knocker.sim_info.sim_id, cls._door_knocker_situation_job, RequestSpawningOption.DONT_CARE, BouncerRequestPriority.EVENT_VIP, expectation_preference=True))
        blacklist = set()
        blacklist.add(door_knocker.sim_info.sim_id)
        kava_carrier_results = services.sim_filter_service().submit_filter(cls._fruitcake_bearer_situation_job.filter, callback=None, requesting_sim_info=active_sim_info, allow_yielding=False, blacklist_sim_ids=blacklist, gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not kava_carrier_results:
            return
        kava_carrier = random.choice(kava_carrier_results)
        guest_list.add_guest_info(SituationGuestInfo(kava_carrier.sim_info.sim_id, cls._fruitcake_bearer_situation_job, RequestSpawningOption.DONT_CARE, BouncerRequestPriority.EVENT_VIP, expectation_preference=True))
        blacklist.add(kava_carrier.sim_info.sim_id)
        lei_carrier_results = services.sim_filter_service().submit_filter(cls._lei_carrier_situation_job.filter, callback=None, requesting_sim_info=active_sim_info, allow_yielding=False, blacklist_sim_ids=blacklist, gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not lei_carrier_results:
            return
        lei_carrier = random.choice(lei_carrier_results)
        guest_list.add_guest_info(SituationGuestInfo(lei_carrier.sim_info.sim_id, cls._lei_carrier_situation_job, RequestSpawningOption.DONT_CARE, BouncerRequestPriority.EVENT_VIP, expectation_preference=True))
        blacklist.add(lei_carrier.sim_info.sim_id)
        other_neighbors_results = services.sim_filter_service().submit_filter(cls._other_neighbors_job.filter, callback=None, requesting_sim_info=active_sim_info, allow_yielding=False, blacklist_sim_ids=blacklist, gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not other_neighbors_results:
            return guest_list
        if len(other_neighbors_results) > cls._number_of_neighbors:
            neighbors = random.sample(other_neighbors_results, cls._number_of_neighbors)
        else:
            neighbors = other_neighbors_results
        for neighbor in neighbors:
            guest_list.add_guest_info(SituationGuestInfo(neighbor.sim_info.sim_id, cls._other_neighbors_job, RequestSpawningOption.DONT_CARE, BouncerRequestPriority.EVENT_VIP, expectation_preference=True))
        return guest_list
示例#28
0
 def __init__(self, **kwargs):
     super().__init__(job_list=TunableMapping(description='A list of roles associated with the situation.', key_type=TunableReference(services.situation_job_manager(), description='Job reference'), value_type=TunableReference(services.get_instance_manager(sims4.resources.Types.ROLE_STATE), description='Role the job will perform'), key_name='job', value_name='role'), exit_conditions=TunableList(TunableTuple(conditions=TunableList(TunableSituationCondition(description='A condition for a situation or single phase.'), description='A list of conditions that all must be satisfied for the group to be considered satisfied.')), description='A list of condition groups of which if any are satisfied, the group is satisfied.'), duration=TunableSimMinute(description='\n                                                    How long the phase will last in sim minutes.\n                                                    0 means forever, which should be used on the last phase of the situation.\n                                                    ', default=60), **kwargs)