class OpenStreetsAutonomySituation(
        WalkbyLimitingTagsMixin,
        situations.situation_complex.SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'role':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                  The situation job for the sim.\n                  '
            ),
            do_stuff_role_state=RoleState.TunableReference(
                description=
                '\n                  The role state for the sim doing stuff.  This is the initial state.\n                  '
            ),
            leave_role_state=RoleState.TunableReference(
                description=
                '\n                  The role state for the sim leaving.\n                  '
            )),
        'do_stuff_timeout':
        sims4.tuning.tunable.TunableSimMinute(
            description=
            '\n            The amount of time the sim does stuff before leaving.\n            ',
            default=180)
    }
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _DoStuffState),
                SituationStateData(2, _LeaveState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.role.situation_job, cls.role.do_stuff_role_state)]

    @classmethod
    def default_job(cls):
        return cls.role.situation_job

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

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.OPEN_STREETS

    def _get_remaining_time_for_gsi(self):
        if self._cur_state is not None:
            return self._cur_state._get_remaining_time_for_gsi()
        return super()._get_remaining_time_for_gsi()
Exemplo n.º 2
0
class GhostSituation(situations.situation_complex.SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'role':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                The situation job for the sim.\n                '
            ),
            do_stuff_role_state=RoleState.TunableReference(
                description=
                '\n                The role state for the sim doing stuff.  This is the initial state.\n                '
            ),
            leave_role_state=RoleState.TunableReference(
                description=
                '\n                The role state for the sim leaving.\n                '
            )),
        'do_stuff_timeout':
        sims4.tuning.tunable.TunableSimMinute(
            description=
            '\n            The amount of time the sim does stuff before leaving.\n            ',
            default=360)
    }
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _BeGhostState),
                SituationStateData(2, _LeaveState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.role.situation_job, cls.role.do_stuff_role_state)]

    @classmethod
    def default_job(cls):
        return cls.role.situation_job

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

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1

    @classmethod
    def _can_start_walkby(cls, lot_id: int):
        return True

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.LOT
Exemplo n.º 3
0
class PatientEmergencySituation(PatientSituationBase):
    INSTANCE_TUNABLES = {'procedure_role_state': RoleState.TunableReference(description='\n            A reference to the hospital patients diagnosed\n            role state while in the situation. This is\n            the state where the patient has been diagnosed \n            but it still waiting for the doctor to treat\n            them.\n            ', tuning_group=PatientSituationBase.JOB_AND_STATE_GROUP, display_name='03_procedure_role_state'), 'procedure_duration_for_time_jump': TunableSimMinute(description='\n            The amount of time allowed to pass before a Sim in the procedure\n            state will be ignored on load with a time jump.\n            ', default=180, tuning_group=PatientSituationBase.TIMEOUT_GROUP), 'force_patient_on_active_career_sim': Tunable(description='\n            If true then the patient will be the active career sim, otherwise\n            we will let the filter service select a sim.\n            ', tunable_type=bool, default=False, tuning_group=PatientSituationBase.JOB_AND_STATE_GROUP)}

    @classmethod
    def _states(cls):
        return (SituationStateData(1, ArrivingState), SituationStateData(2, WaitingState), SituationStateData(3, _ProcedureState), SituationStateData(4, TreatedState))

    def _on_done_waiting(self):
        self._change_state(_ProcedureState())

    @classmethod
    def should_state_type_load_after_time_jump(cls, state_type):
        if not super().should_state_type_load_after_time_jump(state_type):
            return False
        elif state_type is _ProcedureState:
            elapsed_time = services.current_zone().time_elapsed_since_last_save().in_minutes()
            if elapsed_time >= cls.procedure_duration_for_time_jump:
                return False
        return True

    def waiting_expired(self):
        with telemetry_helper.begin_hook(emergency_telemetry_writer, TELEMETRY_EMERGENCY_FAIL) as hook:
            hook.write_guid('type', self.guid64)

    @classmethod
    def get_predefined_guest_list(cls):
        if not cls.force_patient_on_active_career_sim:
            return
        career = services.get_career_service().get_career_in_career_event()
        if career is None:
            return
        guest_list = SituationGuestList(invite_only=True)
        guest_list.add_guest_info(SituationGuestInfo(career.sim_info.sim_id, cls.default_job(), RequestSpawningOption.CANNOT_SPAWN, BouncerRequestPriority.EVENT_VIP))
        return guest_list
Exemplo n.º 4
0
class VoodooSummonSituation(situations.situation_complex.SituationComplexCommon
                            ):
    __qualname__ = 'VoodooSummonSituation'
    INSTANCE_TUNABLES = {
        'summoned_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                          A reference to the SituationJob used for the Sim summoned.\n                          '
            ),
            come_to_me_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling the summoned sim to come here.\n                          '
            )),
        'come_here_affordance':
        sims4.tuning.tunable.TunableReference(
            services.affordance_manager(),
            description='SI to bring summoned sim to the summoner.')
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_buff', '_cost', '_NPC_host_filter', '_NPC_hosted_player_tests',
        'NPC_hosted_situation_start_message',
        'NPC_hosted_situation_use_player_sim_as_filter_requester',
        'NPC_hosted_situation_player_job', 'venue_types',
        'venue_invitation_message', 'venue_situation_player_job', 'category',
        'main_goal', 'minor_goal_chains', 'max_participants',
        '_initiating_sim_tests', '_icon', 'targeted_situation',
        '_resident_job', 'situation_description', 'job_display_ordering',
        'entitlement', '_jobs_to_put_in_party',
        '_relationship_between_job_members', 'main_goal_audio_sting',
        'audio_sting_on_start', '_level_data', '_display_name')

    @staticmethod
    def _states():
        return [(1, _ComeHereState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.summoned_job.situation_job,
                 cls.summoned_job.come_to_me_state)]

    @classmethod
    def default_job(cls):
        return cls.summoned_job.situation_job

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

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._summoned_sim = sim

    def _on_sim_removed_from_situation_prematurely(self, sim):
        super()._on_sim_removed_from_situation_prematurely(sim)
        self._summoned_sim = None
Exemplo n.º 5
0
class SingleJobSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'job':
        TunableTuple(
            description=
            '\n            The job and role which the career Sim is placed into.\n            ',
            situation_job=SituationJob.TunableReference(
                description=
                '\n                A reference to a SituationJob that can be performed at this Situation.\n                '
            ),
            role_state=RoleState.TunableReference(
                description=
                '\n                A role state the Sim assigned to the job will perform.\n                '
            ))
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

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

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

    def start_situation(self):
        super().start_situation()
        self._change_state(SingleJobSituationState())
class StayTheNightSituation(VisitingNPCSituation):
    __qualname__ = 'StayTheNightSituation'
    INSTANCE_TUNABLES = {
        'invited_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                          The situation job for the sim spending the night.\n                          '
            ),
            staying_role_state=RoleState.TunableReference(
                description=
                '\n                          The role state for the sim spending the night.\n                          '
            )),
        'when_to_leave':
        tunable_time.TunableTimeOfDay(
            description=
            '\n            The time of day for the invited sim to leave.\n            ',
            default_hour=7)
    }

    @staticmethod
    def _states():
        return [(1, _StayState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.invited_job.situation_job,
                 cls.invited_job.staying_role_state)]

    @classmethod
    def default_job(cls):
        return cls.invited_job.situation_job

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

    def start_situation(self):
        super().start_situation()
        self._start_time = services.time_service().sim_now
        self._change_state(_StayState())

    def _get_duration(self):
        if self._seed.duration_override is not None:
            return self._seed.duration_override
        time_span = self._start_time.time_till_next_day_time(
            self.when_to_leave)
        return time_span.in_minutes()
class VoodooSummonSituation(situations.situation_complex.SituationComplexCommon
                            ):
    INSTANCE_TUNABLES = {
        'summoned_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                          A reference to the SituationJob used for the Sim summoned.\n                          '
            ),
            come_to_me_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling the summoned sim to come here.\n                          '
            )),
        'come_here_affordance':
        sims4.tuning.tunable.TunableReference(
            services.affordance_manager(),
            description='SI to bring summoned sim to the summoner.')
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.summoned_job.situation_job,
                 cls.summoned_job.come_to_me_state)]

    @classmethod
    def default_job(cls):
        return cls.summoned_job.situation_job

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

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._summoned_sim = sim

    def _on_sim_removed_from_situation_prematurely(self, sim, sim_job):
        self._summoned_sim = None
Exemplo n.º 8
0
class _SingleJobComplexSituationState(CommonSituationState):
    FACTORY_TUNABLES = {
        'role_state':
        RoleState.TunableReference(
            description=
            '\n            The role the Sim has while in this state.\n            \n            This is the initial state.\n            '
        ),
        'locked_args': {
            'job_and_role_changes': frozendict()
        }
    }

    def __init__(self, *args, situation_job, role_state, job_and_role_changes,
                 **kwargs):
        super().__init__(*args,
                         job_and_role_changes={situation_job: role_state},
                         **kwargs)
Exemplo n.º 9
0
class DJSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'job':
        TunableTuple(
            description=
            '\n            The job and role which the career Sim is placed into.\n            ',
            situation_job=SituationJob.TunableReference(
                description=
                '\n                A reference to a SituationJob that can be performed at this Situation.\n                '
            ),
            role_state=RoleState.TunableReference(
                description=
                '\n                A role state the Sim assigned to the job will perform.\n                '
            )),
        'can_start_tag':
        TunableEnumEntry(
            description=
            '\n            A specific tag that an object on this lot must have for this\n            situation to be allowed to start.\n            ',
            tunable_type=Tag,
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ))
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

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

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def situation_meets_starting_requirements(cls, **kwargs):
        object_manager = services.object_manager()
        for _ in object_manager.get_objects_with_tag_gen(cls.can_start_tag):
            return True
        return False

    def start_situation(self):
        super().start_situation()
        self._change_state(_DJSituationState())
Exemplo n.º 10
0
    class _EmployeeSituationState(HasTunableFactory, AutoFactoryInit,
                                  SituationState):
        FACTORY_TUNABLES = {
            'role_state':
            RoleState.TunableReference(
                description=
                '\n                The role state that is active on the employee for the duration\n                of this state.\n                '
            ),
            'timeout_min':
            TunableSimMinute(
                description=
                '\n                The minimum amount of time, in Sim minutes, the employee will be\n                in this state before moving on to a new state.\n                ',
                default=10),
            'timeout_max':
            TunableSimMinute(
                description=
                '\n                The maximum amount of time, in Sim minutes, the employee will be\n                in this state before moving on to a new state.\n                ',
                default=30),
            'push_interaction':
            TunableInteractionOfInterest(
                description=
                '\n                If an interaction of this type is run by the employee, this\n                state will activate.\n                '
            )
        }

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

        def on_activate(self, reader=None):
            super().on_activate(reader)
            self.owner._set_job_role_state(self.owner.employee_job,
                                           self.role_state)
            timeout = random.randint(self.timeout_min, self.timeout_max)
            self._create_or_load_alarm(self.state_name,
                                       timeout,
                                       self._timeout_expired,
                                       reader=reader)

        def _timeout_expired(self, *_, **__):
            self._change_state(self.owner._choose_next_state())
Exemplo n.º 11
0
class WalkbyDogWalker(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'group_filter':
        TunableAggregateFilter.TunableReference(
            description=
            '\n            The aggregate filter that we use to find the sims for this\n            situation.\n            '
        ),
        'walk_state':
        WalkbyWalkState.TunableFactory(
            description=
            '\n            A state for getting the Sims to \n            ',
            locked_args={'allow_join_situation': False}),
        'leave_state':
        LeaveState.TunableFactory(
            description=
            '\n            The state for the adoption officer to leave.\n            ',
            locked_args={'allow_join_situation': False}),
        'dog_walker':
        TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                The Situation Job of the dog walker.\n                '
            ),
            initial_role_state=RoleState.TunableReference(
                description=
                '\n                The initial Role State of the dog walker.\n                '
            )),
        'dog':
        TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                The Situation Job of the dog.\n                '
            ),
            initial_role_state=RoleState.TunableReference(
                description=
                '\n                The initial Role State of the dog.\n                '
            )),
        'situation_job_mapping':
        TunableMapping(
            description=
            '\n            A mapping of filter term tag to situation job.\n            \n            The filter term tag is returned as part of the sim filters used to \n            create the guest list for this particular situation.\n            \n            The situation job is the job that the Sim will be assigned to in\n            the background situation.\n            ',
            key_name='filter_tag',
            key_type=TunableEnumEntry(
                description=
                '\n                The filter term tag returned with the filter results.\n                ',
                tunable_type=FilterTermTag,
                default=FilterTermTag.NO_TAG),
            value_name='job',
            value_type=TunableReference(
                description=
                '\n                The job the Sim will receive when added to the this situation.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.SITUATION_JOB))),
        'sim_spawner_tags':
        TunableList(
            description=
            '\n            A list of tags that represent where to spawn Sims for this\n            Situation when they come onto the lot.  This tuning will be used\n            instead of the tuning on the jobs.\n            NOTE: Tags will be searched in order of tuning. Tag [0] has\n            priority over Tag [1] and so on.\n            ',
            tunable=TunableEnumWithFilter(tunable_type=Tag,
                                          default=Tag.INVALID,
                                          filter_prefixes=SPAWN_PREFIX))
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, GetSimsState),
                SituationStateData(2, WalkbyWalkState, factory=cls.walk_state),
                SituationStateData(3, LeaveState, factory=cls.leave_state))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.dog_walker.situation_job,
                 cls.dog_walker.initial_role_state),
                (cls.dog.situation_job, cls.dog.initial_role_state)]

    @classmethod
    def get_predefined_guest_list(cls):
        guest_list = SituationGuestList(invite_only=True)
        situation_manager = services.get_zone_situation_manager()
        instanced_sim_ids = [
            sim.sim_info.id
            for sim in services.sim_info_manager().instanced_sims_gen()
        ]
        household_sim_ids = [
            sim_info.id
            for sim_info in services.active_household().sim_info_gen()
        ]
        auto_fill_blacklist_walker = situation_manager.get_auto_fill_blacklist(
            sim_job=cls.dog_walker.situation_job)
        auto_fill_blacklist_dog = situation_manager.get_auto_fill_blacklist(
            sim_job=cls.dog.situation_job)
        situation_sims = set()
        for situation in situation_manager.get_situations_by_tags(cls.tags):
            situation_sims.update(situation.invited_sim_ids)
        blacklist_sim_ids = set(
            itertools.chain(situation_sims, instanced_sim_ids,
                            household_sim_ids, auto_fill_blacklist_walker,
                            auto_fill_blacklist_dog))
        filter_results = services.sim_filter_service().submit_matching_filter(
            sim_filter=cls.group_filter,
            allow_yielding=False,
            blacklist_sim_ids=blacklist_sim_ids,
            gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not filter_results:
            return
        if len(filter_results) != cls.group_filter.get_filter_count():
            return
        for result in filter_results:
            job = cls.situation_job_mapping.get(result.tag, None)
            if job is None:
                continue
            guest_list.add_guest_info(
                SituationGuestInfo(result.sim_info.sim_id, job,
                                   RequestSpawningOption.DONT_CARE,
                                   job.sim_auto_invite_allow_priority))
        return guest_list

    def start_situation(self):
        super().start_situation()
        if self._guest_list.guest_info_count != self.group_filter.get_filter_count(
        ):
            self._self_destruct()
        else:
            self._change_state(GetSimsState())

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return cls.group_filter.get_filter_count()

    @classmethod
    def _can_start_walkby(cls, lot_id: int):
        return True

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

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    def on_all_sims_spawned(self):
        self._change_state(self.walk_state())

    def _issue_requests(self):
        zone = services.current_zone()
        if SpawnPoint.ARRIVAL_SPAWN_POINT_TAG in self.sim_spawner_tags or SpawnPoint.VISITOR_ARRIVAL_SPAWN_POINT_TAG in self.sim_spawner_tags:
            lot_id = zone.lot.lot_id
        else:
            lot_id = None
        spawn_point = zone.get_spawn_point(
            lot_id=lot_id,
            sim_spawner_tags=self.sim_spawner_tags,
            spawn_point_request_reason=SpawnPointRequestReason.SPAWN)
        super()._issue_requests(spawn_point_override=spawn_point)
class FireSituation(SituationComplexCommon):
    __qualname__ = 'FireSituation'
    INSTANCE_TUNABLES = {
        'victim_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                                A reference to the SituationJob used during a fire.\n                                '
            ),
            fire_panic_state=RoleState.TunableReference(
                description='The state while the sim is panicking due to fire.'
            ),
            fire_unaware_state=RoleState.TunableReference(
                description=
                '\n                                The state while the sim is unaware there is a \n                                fire on the lot.\n                                '
            ),
            fire_aware_state=RoleState.TunableReference(
                description=
                "\n                                The state while the Sim is aware there is a fire\n                                but hasn't seen it yet.\n                                "
            ),
            fire_safe_state=RoleState.TunableReference(
                description=
                '\n                                The state while the Sim has made it safely away\n                                from the fire.\n                                '
            ),
            post_fire_state=RoleState.TunableReference(
                description=
                '\n                                The state the Sim is in after the fire has gone\n                                out.\n                                '
            ),
            tuning_group=GroupNames.SITUATION),
        'got_to_safety_interaction':
        situations.situation_complex.TunableInteractionOfInterest(
            description=
            '\n            The interaction to look for when a Sim has routed off of the lot\n            and safely escaped the fire.\n            '
        ),
        'go_back_to_panic_interactions':
        situations.situation_complex.TunableInteractionOfInterest(
            description=
            '\n            The interactions to look for when a Sim has routed back on to a\n            lot that is on fire which will cause the Sim to go back into panic\n            mode.\n            '
        ),
        'TIME_POST_FIRE_IN_SIM_MINUTES':
        TunableSimMinute(
            description=
            '\n            Number of Sim minutes that the situation can be in the _PostFireState\n            before the situation ends.\n            ',
            default=60)
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_buff', '_cost', '_NPC_host_filter', '_NPC_hosted_player_tests',
        'NPC_hosted_situation_start_message',
        'NPC_hosted_situation_use_player_sim_as_filter_requester',
        'NPC_hosted_situation_player_job', 'venue_types',
        'venue_invitation_message', 'venue_situation_player_job', 'category',
        'main_goal', 'minor_goal_chains', '_level_data', '_display_name',
        'max_participants', '_initiating_sim_tests', 'targeted_situation',
        '_resident_job', '_icon', 'situation_description')

    @staticmethod
    def _states():
        return [(1, _PanicState), (2, _UnawareState), (3, _AlertedState),
                (4, _SafeState), (5, _PostFireState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.victim_job.situation_job, cls.victim_job.fire_panic_state)
                ]

    @classmethod
    def default_job(cls):
        return cls.victim_job.situation_job

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

    def advance_to_alerted(self):
        if type(self._cur_state) != _PanicState:
            self._change_state(_PanicState())

    def advance_to_post_fire(self):
        self._change_state(_PostFireState())

    def on_remove(self):
        fire_service = services.get_fire_service()
        if fire_service is not None:
            for sim in self.all_sims_in_situation_gen():
                fire_service.remove_fire_situation(sim)
        super().on_remove()
Exemplo n.º 13
0
class CaregiverSituation(SituationComplexCommon):
    CAREGIVER_EVENTS = (TestEvent.SituationStarted,
                        TestEvent.AvailableDaycareSimsChanged)
    INSTANCE_TUNABLES = {
        'caregiver_data':
        TunableTuple(
            description=
            '\n            The relationship bits to apply to Sims.\n            ',
            caregiver_bit=TunableReference(
                description=
                "\n                The bit that is applied to Sims that are the situation owner's\n                Sim's caregiver. This is, for example, a bit on an adult\n                targeting a toddler.\n                ",
                manager=services.get_instance_manager(
                    sims4.resources.Types.RELATIONSHIP_BIT)),
            caregiver_job=SituationJob.TunableReference(
                description=
                '\n                The situation job that caregivers are assigned when in this situation.\n                '
            ),
            caregiver_rolestate=RoleState.TunableReference(
                description=
                '\n                The role state that caregivers are assigned when in this situation.\n                '
            ),
            care_dependent_bit=TunableReference(
                description=
                '\n                The bit that is applied to Sims that are the situation owner\n                This is, for example, a bit on a toddler targeting an adult.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.RELATIONSHIP_BIT))),
        'caregiver_relationships':
        TunableSet(
            description=
            '\n            A list of bits that make Sims primary caregivers. If any Sim with\n            any of these bits is instantiated and living in the same household \n            as the care dependent, they are considered caregivers.\n            \n            If no primary caregiver exists, and no caregiver service exists,\n            active TYAE Sims are made caregivers.\n            ',
            tunable=TunableReference(manager=services.get_instance_manager(
                sims4.resources.Types.RELATIONSHIP_BIT),
                                     pack_safe=True))
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return ((cls.caregiver_data.caregiver_job,
                 cls.caregiver_data.caregiver_rolestate), )

    def _is_valid_caregiver(self,
                            care_dependent,
                            caregiver,
                            ignore_zone=False):
        if not ignore_zone and care_dependent.zone_id != caregiver.zone_id:
            return False
        if caregiver.is_toddler_or_younger:
            return False
        if caregiver.is_pet:
            return False
        if care_dependent.household_id == caregiver.household_id and any(
                caregiver.relationship_tracker.has_bit(care_dependent.sim_id,
                                                       rel_bit)
                for rel_bit in self.caregiver_relationships):
            return True
        else:
            daycare_service = services.daycare_service()
            if daycare_service is not None and daycare_service.is_daycare_service_npc_available(
                    sim_info=caregiver, household=care_dependent.household):
                return True
        return False

    def _update_caregiver_status(self):
        care_dependent = self._guest_list.host_sim
        if care_dependent is None:
            return
        if care_dependent.household is None:
            return
        if care_dependent.is_being_destroyed:
            return
        available_sims = tuple(
            sim_info for sim_info in
            services.daycare_service().get_available_sims_gen())
        current_caregivers = set(self._situation_sims)
        for sim in current_caregivers:
            self._pending_caregivers.discard(sim)
        eligible_caregivers = set(
            sim_info for sim_info in available_sims
            if self._is_valid_caregiver(care_dependent, sim_info))
        if not eligible_caregivers:
            eligible_caregivers = set(
                sim_info for sim_info in
                care_dependent.household.can_live_alone_info_gen()
                if sim_info in available_sims)
        for sim in self._pending_caregivers:
            eligible_caregivers.discard(sim.sim_info)
        for potential_caregiver in tuple(eligible_caregivers):
            sim = potential_caregiver.get_sim_instance(
                allow_hidden_flags=ALL_HIDDEN_REASONS)
            if sim is None or sim.is_being_destroyed:
                eligible_caregivers.discard(potential_caregiver)
            else:
                if sim in current_caregivers:
                    continue
                self.invite_sim_to_job(sim,
                                       job=self.caregiver_data.caregiver_job)
                self._pending_caregivers.add(sim)
                care_dependent.relationship_tracker.add_relationship_bit(
                    potential_caregiver.sim_id,
                    self.caregiver_data.care_dependent_bit)
                potential_caregiver.relationship_tracker.add_relationship_bit(
                    care_dependent.sim_id, self.caregiver_data.caregiver_bit)
        for sim in tuple(current_caregivers):
            if sim.sim_info not in eligible_caregivers:
                self._remove_caregiver_rel_bits(care_dependent, sim.sim_info)
                self.remove_sim_from_situation(sim)
                current_caregivers.discard(sim)

    def _remove_caregiver_rel_bits(self, care_dependent, other_sim_info=None):
        if other_sim_info is not None:
            care_dependent.relationship_tracker.remove_relationship_bit(
                other_sim_info.id, self.caregiver_data.care_dependent_bit)
            other_sim_info.relationship_tracker.remove_relationship_bit(
                care_dependent.id, self.caregiver_data.caregiver_bit)
        else:
            for relationship in care_dependent.relationship_tracker:
                other_sim_id = relationship.get_other_sim_id(
                    care_dependent.sim_id)
                relationship.remove_bit(care_dependent.sim_id, other_sim_id,
                                        self.caregiver_data.care_dependent_bit)
                relationship.remove_bit(other_sim_id, care_dependent.sim_id,
                                        self.caregiver_data.caregiver_bit)

    def get_care_dependent_if_last_caregiver(self,
                                             sim_info,
                                             excluding_interaction_types=None):
        care_dependent = self._guest_list.host_sim
        if care_dependent.household.home_zone_id == services.current_zone_id():
            return
        if not care_dependent.relationship_tracker.has_relationship(
                sim_info.id):
            return
        for relationship in care_dependent.relationship_tracker:
            if relationship.get_other_sim_info(
                    care_dependent.sim_id) is sim_info:
                if not relationship.has_bit(
                        care_dependent.sim_id,
                        self.caregiver_data.care_dependent_bit):
                    return
                    if relationship.has_bit(
                            care_dependent.sim_id,
                            self.caregiver_data.care_dependent_bit):
                        if excluding_interaction_types is not None:
                            other_sim = relationship.get_other_sim(
                                care_dependent.sim_id)
                            if other_sim is None:
                                continue
                            if other_sim.has_any_interaction_running_or_queued_of_types(
                                    excluding_interaction_types):
                                continue
                        else:
                            return
            elif relationship.has_bit(care_dependent.sim_id,
                                      self.caregiver_data.care_dependent_bit):
                if excluding_interaction_types is not None:
                    other_sim = relationship.get_other_sim(
                        care_dependent.sim_id)
                    if other_sim is None:
                        continue
                    if other_sim.has_any_interaction_running_or_queued_of_types(
                            excluding_interaction_types):
                        continue
                else:
                    return
        return care_dependent

    def start_situation(self):
        self._update_caregiver_status()
        services.get_event_manager().register(self, self.CAREGIVER_EVENTS)
        return super().start_situation()

    def _destroy(self):
        services.get_event_manager().unregister(self, self.CAREGIVER_EVENTS)
        care_dependent = self._guest_list.host_sim
        self._remove_caregiver_rel_bits(care_dependent)
        super()._destroy()

    def handle_event(self, sim_info, event, resolver):
        super().handle_event(sim_info, event, resolver)
        if event in self.CAREGIVER_EVENTS:
            self._update_caregiver_status()
Exemplo n.º 14
0
class InviteToSituation(situations.situation_complex.SituationComplexCommon):
    __qualname__ = 'InviteToSituation'
    INSTANCE_TUNABLES = {
        'invited_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                          A reference to the SituationJob used for the Sims invited to.\n                          '
            ),
            invited_to_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling a sim to wait. They will momentarily be\n                          pulled from this situation by a visit or venue situation.\n                          '
            )),
        'purpose':
        sims4.tuning.tunable.TunableEnumEntry(
            description=
            '\n                The purpose/reason used to perform the venue specific operation\n                to get this sim in the appropriate situation.\n                This should be tuned to Invite In, but since that is a dynamic enum\n                you must do it yourself.\n                ',
            tunable_type=venues.venue_constants.NPCSummoningPurpose,
            default=venues.venue_constants.NPCSummoningPurpose.DEFAULT)
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_buff', '_cost', '_NPC_host_filter', '_NPC_hosted_player_tests',
        'NPC_hosted_situation_start_message',
        'NPC_hosted_situation_use_player_sim_as_filter_requester',
        'NPC_hosted_situation_player_job', 'venue_types',
        'venue_invitation_message', 'venue_situation_player_job', 'category',
        'main_goal', 'minor_goal_chains', 'max_participants',
        '_initiating_sim_tests', '_icon', 'targeted_situation',
        '_resident_job', 'situation_description', 'job_display_ordering',
        'entitlement', '_jobs_to_put_in_party',
        '_relationship_between_job_members', 'main_goal_audio_sting',
        'audio_sting_on_start', '_level_data', '_display_name')

    @staticmethod
    def _states():
        return [(1, _WaitState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.invited_job.situation_job,
                 cls.invited_job.invited_to_state)]

    @classmethod
    def default_job(cls):
        return cls.invited_job.situation_job

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

    def start_situation(self):
        super().start_situation()
        self.manager.add_pre_bouncer_update(self)
        self._change_state(_WaitState())

    def _issue_requests(self):
        pass

    def on_pre_bouncer_update(self):
        zone = services.current_zone()
        venue = zone.venue_service.venue
        for sim_info in self._seed.invited_sim_infos_gen():
            venue.summon_npcs((sim_info, ), self.purpose)

    @classmethod
    def get_player_greeted_status_from_seed(cls, situation_seed):
        for sim_info in situation_seed.invited_sim_infos_gen():
            while sim_info.is_npc and sim_info.lives_here:
                return GreetedStatus.GREETED
        return GreetedStatus.NOT_APPLICABLE
Exemplo n.º 15
0
class YardSaleSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'user_job':
        TunableTuple(
            description=
            '\n            The job and role which the Sim is placed into.\n            ',
            situation_job=SituationJob.TunableReference(
                description=
                '\n                A reference to a SituationJob that can be performed at this Situation.\n                '
            ),
            role_state=RoleState.TunableReference(
                description=
                '\n                A role state the sim assigned to the job will perform.\n                '
            )),
        'manage_customers_state':
        ManageCustomersState.TunableFactory(
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP),
        'customer_situation':
        Situation.TunableReference(
            description=
            '\n            Customer Situation to spawn that will pull customers to purchase\n            items from the craft sales table.\n            ',
            class_restrictions=('YardSaleCustomerSituation', )),
        'number_of_expected_customers':
        TunableInterval(
            description=
            '\n            The number of customers we expect to have at any given time the\n            yard sale is running. The yard sale will attempt to manage this\n            many customer situations at any given time.\n            ',
            tunable_type=int,
            default_lower=0,
            default_upper=10,
            minimum=0,
            maximum=10)
    }

    def __init__(self, *arg, **kwargs):
        super().__init__(*arg, **kwargs)
        self.scoring_enabled = False
        reader = self._seed.custom_init_params_reader
        if reader is None:
            self.customer_situations = []
        else:
            self.customer_situations = list(
                reader.read_uint32s(CUSTOMER_SITUATIONS_TOKEN, list()))

    @classmethod
    def default_job(cls):
        pass

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.user_job.situation_job, cls.user_job.role_state)]

    @classmethod
    def _states(cls):
        return (SituationStateData(1,
                                   ManageCustomersState,
                                   factory=cls.manage_customers_state), )

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

    def _self_destruct(self):
        situation_manager = services.get_zone_situation_manager()
        for situation_id in self.customer_situations:
            situation = situation_manager.get(situation_id)
            if situation is not None:
                situation._self_destruct()
        self.customer_situations.clear()
        super()._self_destruct()

    def get_customer_situations(self):
        customers = []
        situation_manager = services.get_zone_situation_manager()
        for situation_id in self.customer_situations:
            situation = situation_manager.get(situation_id)
            if situation is not None:
                customers.append(situation)
        self.customer_situations = [situation.id for situation in customers]
        return self.customer_situations

    def create_customer_situation(self):
        situation_manager = services.get_zone_situation_manager()
        situation_id = situation_manager.create_situation(
            self.customer_situation, guest_list=None, user_facing=False)
        self.customer_situations.append(situation_id)
class ClubGatheringSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'_default_job': SituationJob.TunableReference(description='\n            The default job for all members of this situation.\n            '), '_default_role_state': RoleState.TunableReference(description='\n            The Role State for Sims in the default job of this situation.\n            '), '_default_gathering_vibe': TunableEnumEntry(description='\n            The default Club vibe to use for the gathering.\n            ', tunable_type=ClubGatheringVibe, default=ClubGatheringVibe.NO_VIBE), '_vibe_buffs': TunableMapping(description="        \n            A Mapping of ClubGatheringVibe to List of buffs.\n            \n            When setting the vibe for the gathering the type is found in the\n            mapping and then each buff is processed in order until one of them\n            can be added. Then evaluation stops.\n            \n            Example: The club vibe is getting set to ClubGatheringVibe.Angry.\n            That entry has 3 buffs associated with it in the mapping. Angry3,\n            Angry2, Angry1 in that order. Angry3 doesn't pass evaluation so it\n            is passed. Next Angry2 does pass evaluation and so we add Angry2\n            Vibe Buff to the gathering. Angry1 is never evaluated in this\n            situation. Angry1 is only ever evaluated if Angry3 and Angry2 both\n            fail.\n            ", key_type=ClubGatheringVibe, value_type=TunableList(description='\n                A List of buff to attempt to use on the gathering. Order is\n                important as we do not try to give any buffs after one is given\n                to the gathering.\n                ', tunable=Buff.TunableReference(), minlength=1)), '_gathering_buff_reason': TunableLocalizedString(description='\n            The reason the gathering buff was added. Displayed on the buff\n            tooltip.\n            '), '_initial_disband_timer': TunableSimMinute(description='\n            The number of Sim minutes after a Gathering is created before it\n            can disband due to lack of members.\n            ', default=30, minimum=1), '_initial_notification': TunableUiDialogNotificationSnippet(description='\n            A notification that shows up once the gathering starts.\n            '), '_minimum_number_of_sims': TunableRange(description='\n            The minimum number of Sims that must be present in a Gathering to\n            keep it from disbanding.\n            ', tunable_type=int, default=3, minimum=2), 'time_between_bucks_rewards': TunableSimMinute(description='\n            The time in Sim Minutes to wait before awarding\n            the first club bucks for being in a gathering.\n            ', default=10), 'reward_bucks_per_interval': Tunable(description='\n            The amount of Club Bucks to award to the associated club at each \n            tuned interval.\n            ', tunable_type=int, default=1), 'rule_breaking_buff': TunableBuffReference(description='\n            Award this buff whenever a Sim breaks the rules.\n            ')}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    def __init__(self, seed):
        super().__init__(seed)
        self.associated_club = None
        self._current_gathering_buff = None
        self._current_gathering_vibe = None
        self._sim_gathering_time_checks = {}
        self._can_disband = False
        self._initial_disband_timer_handle = None
        self._rewards_timer = None
        self._time_tracker_timer = None
        self._validity_household_id_override = None
        reader = self._seed.custom_init_params_reader
        if reader is not None:
            start_source = reader.read_uint64(ClubGatheringKeys.START_SOURCE, None)
            disband_ticks = reader.read_uint64(ClubGatheringKeys.DISBAND_TICKS, 0)
            self._validity_household_id_override = reader.read_uint64(ClubGatheringKeys.HOUSEHOLD_ID_OVERRIDE, None)
            associated_club_id = reader.read_uint64(ClubGatheringKeys.ASSOCIATED_CLUB_ID, None)
            if associated_club_id is not None:
                club_service = services.get_club_service()
                associated_club = club_service.get_club_by_id(associated_club_id)
                self.initialize_gathering(associated_club, disband_ticks=disband_ticks, start_source=start_source)
            current_gathering_buff_guid = reader.read_uint64(ClubGatheringKeys.GATHERING_BUFF, 0)
            self._current_gathering_buff = services.get_instance_manager(sims4.resources.Types.BUFF).get(current_gathering_buff_guid)
            vibe = reader.read_uint64(ClubGatheringKeys.GATHERING_VIBE, self._default_gathering_vibe)
            self.set_club_vibe(vibe)

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.default_job(), cls._default_role_state)]

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

    def _destroy(self):
        if self._initial_disband_timer_handle is not None:
            self._initial_disband_timer_handle.cancel()
            self._initial_disband_timer_handle = None
        if self._rewards_timer is not None:
            self._rewards_timer.cancel()
            self._rewards_timer = None
        if self._time_tracker_timer is not None:
            self._time_tracker_timer.cancel()
            self._time_tracker_timer = None
        super()._destroy()

    def _disband_timer_callback(self, _):
        self._can_disband = True
        if self._initial_disband_timer_handle is not None:
            alarms.cancel_alarm(self._initial_disband_timer_handle)
            self._initial_disband_timer_handle = None
        self._disband_if_neccessary()

    def _disband_if_neccessary(self):
        if not self._can_disband:
            return
        if len(list(self.all_sims_in_situation_gen())) < self._minimum_number_of_sims:
            self._self_destruct()

    def on_remove(self):
        self._can_disband = False
        super().on_remove()
        self._cleanup_gathering()

    def _cleanup_gathering(self):
        club_service = services.get_club_service()
        if club_service is None:
            logger.error("Attempting to end a Gathering but the ClubService doesn't exist.")
            return
        op = EndClubGathering(self.associated_club.club_id)
        Distributor.instance().add_op_with_no_owner(op)
        club_service.on_gathering_ended(self)

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

    def load_situation(self):
        result = super().load_situation()
        if result and (self.associated_club is None or not (not self.is_validity_overridden() and not self.associated_club.is_zone_valid_for_gathering(services.current_zone_id()))):
            self._cleanup_gathering()
            return False
        return result

    def is_validity_overridden(self):
        return self._validity_household_id_override == services.active_household_id() and services.active_household_lot_id() == services.active_lot_id()

    def initialize_gathering(self, associated_club, disband_ticks=None, start_source=None):
        club_service = services.get_club_service()
        if club_service is None:
            logger.error("Attempting to start a Gathering but the ClubService doesn't exist.")
            return
        self.associated_club = associated_club
        if start_source is not None:
            if start_source == ClubGatheringStartSource.APPLY_FOR_INVITE:
                invited_sim = services.sim_info_manager().get(self._guest_list.host_sim_id)
                self.associated_club.show_club_notification(invited_sim, ClubTunables.CLUB_GATHERING_START_DIALOG)
            elif any(sim_info.is_selectable for sim_info in self._guest_list.invited_sim_infos_gen()):
                initial_notification = self._initial_notification(services.active_sim_info())
                initial_notification.show_dialog(icon_override=IconInfoData(icon_resource=associated_club.icon), additional_tokens=(associated_club.name,))
            self._initial_disband_timer_handle = alarms.add_alarm(self.associated_club, interval_in_sim_minutes(self._initial_disband_timer), self._disband_timer_callback)
        elif disband_ticks > 0:
            self._initial_disband_timer_handle = alarms.add_alarm(self.associated_club, clock.TimeSpan(disband_ticks), self._disband_timer_callback)
        time_between_rewards = create_time_span(minutes=self.time_between_bucks_rewards)
        self._rewards_timer = alarms.add_alarm(self, time_between_rewards, self._award_club_bucks, True)
        time_between_gathering_checks = create_time_span(minutes=club_tuning.ClubTunables.MINUTES_BETWEEN_CLUB_GATHERING_PULSES)
        self._time_tracker_timer = alarms.add_alarm(self, time_between_gathering_checks, self._add_time_in_gathering, True)
        op = StartClubGathering(self.associated_club.club_id)
        Distributor.instance().add_op_with_no_owner(op)
        club_service.on_gathering_started(self)

    def _on_minimum_number_of_members_reached(self):
        self._can_disband = True
        if self._initial_disband_timer_handle is not None:
            alarms.cancel_alarm(self._initial_disband_timer_handle)
            self._initial_disband_timer_handle = None

    def _on_add_sim_to_situation(self, sim, *args, **kwargs):
        super()._on_add_sim_to_situation(sim, *args, **kwargs)
        club_service = services.get_club_service()
        if club_service is None:
            logger.error("Attempting to add a Sim to a Gathering but the ClubService doesn't exist.")
            return
        club_service.on_sim_added_to_gathering(sim, self)
        self.add_club_vibe_buff_to_sim(sim)
        relationship_tracker = sim.relationship_tracker
        relationship_tracker.add_create_relationship_listener(self._relationship_added_callback)
        sim.sim_info.register_for_outfit_changed_callback(self._on_outfit_changed)
        if not self._can_disband and len(list(self.all_sims_in_situation_gen())) >= self._minimum_number_of_sims:
            self._on_minimum_number_of_members_reached()
        op = UpdateClubGathering(GatheringUpdateType.ADD_MEMBER, self.associated_club.club_id, sim.id)
        Distributor.instance().add_op_with_no_owner(op)
        if self.associated_club.member_should_spin_into_club_outfit(sim):
            self._push_spin_into_current_outfit_interaction(sim)
        self._sim_gathering_time_checks[sim] = services.time_service().sim_timeline.now

    def _on_remove_sim_from_situation(self, sim):
        super()._on_remove_sim_from_situation(sim)
        sim.remove_buff_by_type(self._current_gathering_buff)
        self._disband_if_neccessary()
        club_service = services.get_club_service()
        if club_service is None:
            logger.error("Attempting to add a Sim to a Gathering but the ClubService doesn't exist.")
            return
        club_service.on_sim_removed_from_gathering(sim, self)
        relationship_tracker = sim.relationship_tracker
        relationship_tracker.remove_create_relationship_listener(self._relationship_added_callback)
        sim.sim_info.unregister_for_outfit_changed_callback(self._on_outfit_changed)
        if self.associated_club.member_should_spin_into_club_outfit(sim):
            sim.sim_info.register_for_outfit_changed_callback(self._on_outfit_removed)
            self._push_spin_into_current_outfit_interaction(sim)
        else:
            self._remove_apprearance_modifiers(sim.sim_info)
        if self.associated_club in club_service.clubs_to_gatherings_map:
            op = UpdateClubGathering(GatheringUpdateType.REMOVE_MEMBER, self.associated_club.club_id, sim.id)
            Distributor.instance().add_op_with_no_owner(op)
        if sim in self._sim_gathering_time_checks:
            self._process_time_in_gathering_event(sim)
            del self._sim_gathering_time_checks[sim]

    def set_club_vibe(self, vibe):
        self._current_gathering_vibe = vibe
        vibe_buffs = self._vibe_buffs.get(vibe, ())
        member = self.associated_club.members[0]
        for buff in vibe_buffs:
            if buff.can_add(member):
                if buff is not self._current_gathering_buff:
                    for sim in self.all_sims_in_situation_gen():
                        sim.remove_buff_by_type(self._current_gathering_buff)
                        self.add_club_vibe_buff_to_sim(sim, buff)
                    self._current_gathering_buff = buff
                return

    def add_club_vibe_buff_to_sim(self, sim, buff=None):
        buff = self._current_gathering_buff if buff is None else buff
        if buff is None:
            return
        if sim.has_buff(buff):
            return
        sim.add_buff(buff, self._gathering_buff_reason)

    def _relationship_added_callback(self, relationship):
        resolver = DoubleSimResolver(relationship.find_sim_info_a(), relationship.find_sim_info_b(), additional_participants={ParticipantType.AssociatedClub: (self.associated_club,)})
        for (perk, benefit) in ClubTunables.NEW_RELATIONSHIP_MODS.items():
            if self.associated_club.bucks_tracker.is_perk_unlocked(perk):
                if not benefit.test_set.run_tests(resolver=resolver):
                    continue
                benefit.loot.apply_to_resolver(resolver=resolver)

    def _award_club_bucks(self, handle):
        qualified_sims = [sim for sim in self._situation_sims if self._sim_satisfies_requirement_for_bucks(sim)]
        if not qualified_sims:
            return
        if any(sim for sim in self._situation_sims if club_tuning.ClubTunables.CLUB_BUCKS_REWARDS_MULTIPLIER.trait in sim.sim_info.trait_tracker.equipped_traits):
            multiplier = club_tuning.ClubTunables.CLUB_BUCKS_REWARDS_MULTIPLIER.multiplier
        else:
            multiplier = 1
        bucks_tracker = self.associated_club.bucks_tracker
        if bucks_tracker is None:
            return
        bucks_tracker.try_modify_bucks(ClubTunables.CLUB_BUCKS_TYPE, int(self.reward_bucks_per_interval*multiplier), reason='Time in club gathering')

    def _sim_satisfies_requirement_for_bucks(self, sim):
        if not sim.is_selectable:
            return False
        elif not sim.sim_info.is_instanced():
            return False
        return True

    def _on_outfit_changed(self, sim_info, outfit_category_and_index):
        club = self.associated_club
        (cas_parts_add, cas_parts_remove) = club.get_club_outfit_parts(sim_info, outfit_category_and_index)
        appearance_tracker = sim_info.appearance_tracker
        appearance_tracker.remove_appearance_modifiers(self.guid, source=self)
        modifiers = []
        for cas_part in cas_parts_add:
            modifier = AppearanceModifier.SetCASPart(cas_part=cas_part, should_toggle=False, replace_with_random=False, update_genetics=False, _is_combinable_with_same_type=True, remove_conflicting=False, outfit_type_compatibility=None)
            modifiers.append(modifier)
        for cas_part in cas_parts_remove:
            modifier = AppearanceModifier.SetCASPart(cas_part=cas_part, should_toggle=True, replace_with_random=False, update_genetics=False, _is_combinable_with_same_type=True, remove_conflicting=False, outfit_type_compatibility=None)
            modifiers.append(modifier)
        for modifier in modifiers:
            appearance_tracker.add_appearance_modifier(modifier, self.guid, 1, False, source=self)
        appearance_tracker.evaluate_appearance_modifiers()
        if sim_info.appearance_tracker.appearance_override_sim_info is not None:
            sim = sim_info.get_sim_instance()
            if sim is not None:
                sim.apply_outfit_buffs_for_sim_info(sim_info.appearance_tracker.appearance_override_sim_info, outfit_category_and_index)

    def _on_outfit_removed(self, sim_info, outfit_category_and_index):
        self._remove_apprearance_modifiers(sim_info)

    def _remove_apprearance_modifiers(self, sim_info):
        sim_info.appearance_tracker.remove_appearance_modifiers(self.guid, source=self)
        sim_info.unregister_for_outfit_changed_callback(self._on_outfit_removed)

    def _push_spin_into_current_outfit_interaction(self, sim):
        sim.sim_info.set_outfit_dirty(sim.get_current_outfit()[0])
        change_outfit_context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, priority.Priority.High)
        return sim.push_super_affordance(ForceChangeToCurrentOutfit, None, change_outfit_context)

    def remove_all_club_outfits(self):
        for sim in self.all_sims_in_situation_gen():
            self._push_spin_into_current_outfit_interaction(sim)

    def _add_time_in_gathering(self, handle):
        qualified_sims = [sim for sim in self._situation_sims if self._sim_satisfies_requirement_for_bucks(sim)]
        if not qualified_sims:
            return
        now = services.time_service().sim_timeline.now
        for sim in qualified_sims:
            self._process_time_in_gathering_event(sim, now)
            self._sim_gathering_time_checks[sim] = now

    def _process_time_in_gathering_event(self, sim, now=None):
        if now is None:
            now = services.time_service().sim_timeline.now
        elapsed_time = now - self._sim_gathering_time_checks[sim]
        services.get_event_manager().process_event(test_events.TestEvent.TimeInClubGathering, sim_info=sim.sim_info, amount=int(elapsed_time.in_minutes()))

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        writer.write_uint64(ClubGatheringKeys.ASSOCIATED_CLUB_ID, self.associated_club.club_id)
        if self._initial_disband_timer_handle is not None:
            current_time = services.time_service().sim_now
            disband_ticks = max((self._initial_disband_timer_handle.finishing_time - current_time).in_ticks(), 0)
        else:
            disband_ticks = 0
        writer.write_uint64(ClubGatheringKeys.DISBAND_TICKS, disband_ticks)
        if self._current_gathering_buff is not None:
            writer.write_uint64(ClubGatheringKeys.GATHERING_BUFF, self._current_gathering_buff.guid64)
        writer.write_uint64(ClubGatheringKeys.GATHERING_VIBE, self._current_gathering_vibe)
        if self._validity_household_id_override is not None:
            writer.write_uint64(ClubGatheringKeys.HOUSEHOLD_ID_OVERRIDE, self._validity_household_id_override)

    def _issue_requests(self):
        super()._issue_requests()
        request = AssociatedClubRequestFactory(self, callback_data=_RequestUserData(), job_type=self._default_job, request_priority=BouncerRequestPriority.EVENT_DEFAULT_JOB, user_facing=False, exclusivity=self.exclusivity)
        self.manager.bouncer.submit_request(request)
Exemplo n.º 17
0
class FireSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'victim_job': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='\n                                A reference to the SituationJob used during a fire.\n                                '), fire_panic_state=RoleState.TunableReference(description='The state while the sim is panicking due to fire.'), fire_unaware_state=RoleState.TunableReference(description='\n                                The state while the sim is unaware there is a \n                                fire on the lot.\n                                '), fire_safe_state=RoleState.TunableReference(description='\n                                The state while the Sim has made it safely away\n                                from the fire.\n                                '), post_fire_state=RoleState.TunableReference(description='\n                                The state the Sim is in after the fire has gone\n                                out.\n                                '), save_toddler_state=RoleState.TunableReference(description='\n                                The state the Sim is in while they are saving\n                                a toddler.\n                                '), tuning_group=GroupNames.SITUATION), 'got_to_safety_interaction': situations.situation_complex.TunableInteractionOfInterest(description='\n            The interaction to look for when a Sim has routed off of the lot\n            and safely escaped the fire.\n            '), 'panic_interaction': situations.situation_complex.TunableInteractionOfInterest(description='\n            The interaction that a sim runs while panicking. \n            '), 'go_back_to_panic_interactions': situations.situation_complex.TunableInteractionOfInterest(description='\n            The interactions to look for when a Sim has routed back on to a\n            lot that is on fire which will cause the Sim to go back into panic\n            mode.\n            '), 'save_toddler_interaction': TunableReference(description='\n            The interaction to push on a Sim to save a toddler from the fire.\n            ', manager=services.get_instance_manager(sims4.resources.Types.INTERACTION), class_restrictions='SuperInteraction'), 'TIME_POST_FIRE_IN_SIM_MINUTES': TunableSimMinute(description='\n            Number of Sim minutes that the situation can be in the _PostFireState\n            before the situation ends.\n            ', default=60)}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _PanicState), SituationStateData(2, _UnawareState), SituationStateData(3, _SafeState), SituationStateData(4, _PostFireState), SituationStateData(5, _SaveToddlerState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.victim_job.situation_job, cls.victim_job.fire_panic_state)]

    @classmethod
    def default_job(cls):
        return cls.victim_job.situation_job

    @classproperty
    def has_no_klout(cls):
        return True

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

    def advance_to_panic(self):
        curr_state_type = type(self._cur_state)
        if curr_state_type == _UnawareState or curr_state_type == _PostFireState:
            self._change_state(_PanicState())

    def advance_to_post_fire(self):
        self._change_state(_PostFireState())

    def reset_to_unaware(self):
        if type(self._cur_state) != _UnawareState:
            self._cancel_duration_alarm()
            self._change_state(_UnawareState())

    def on_remove(self):
        fire_service = services.get_fire_service()
        if fire_service is not None:
            for sim in self.all_sims_in_situation_gen():
                fire_service.remove_fire_situation(sim)
        super().on_remove()
class AggregateSingleStateSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'group_filter': TunableAggregateFilter.TunableReference(description='\n            The aggregate filter that we use to find the sims for this\n            situation.\n            '), 'situation_job_mapping': TunableMapping(description='\n            A mapping of filter term tag to situation job.\n            \n            The filter term tag is returned as part of the sim filters used to \n            create the guest list for this particular background situation.\n            \n            The situation job is the job that the Sim will be assigned to in\n            the background situation.\n            ', key_name='filter_tag', key_type=TunableEnumEntry(description='\n                The filter term tag returned with the filter results.\n                ', tunable_type=FilterTermTag, default=FilterTermTag.NO_TAG), value_name='job_and_role', value_type=TunableTuple(description='\n                The job and role state that the Sim will be put into.\n                ', situation_job=SituationJob.TunableReference(description='\n                    A reference to a SituationJob that can be performed at this Situation.\n                    '), role_state=RoleState.TunableReference(description='\n                    A role state the Sim assigned to the job will perform.\n                    '))), 'blacklist_job': SituationJob.TunableReference(description='\n            The default job used for blacklisting Sims from being put into this\n            AggregateSingleStateSituation.\n            '), 'force_leave_on_exit': Tunable(description='\n            If checked, then we will force the Sims to leave when they are\n            removed from the situation.  Otherwise we will just let the leave\n            Situation pick them up as normal.\n            ', tunable_type=bool, default=False)}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _verify_tuning_callback(cls):
        super()._verify_tuning_callback()
        for job_role in cls.situation_job_mapping.values():
            if job_role.situation_job.sim_auto_invite.upper_bound > 0:
                logger.error('Situation Job {} used for an aggregate filter specifies Sim Auto Invite.  This is not supported and can cause errors.', job_role.situation_job)

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(job_role.situation_job, job_role.role_state) for job_role in cls.situation_job_mapping.values()]

    @classmethod
    def get_predefined_guest_list(cls):
        guest_list = SituationGuestList(invite_only=True)
        situation_manager = services.get_zone_situation_manager()
        instanced_sim_ids = [sim.sim_id for sim in services.sim_info_manager().instanced_sims_gen()]
        household_sim_ids = [sim_info.id for sim_info in services.active_household().sim_info_gen()]
        auto_fill_blacklist = situation_manager.get_auto_fill_blacklist(sim_job=cls.blacklist_job)
        situation_sims = set()
        for situation in situation_manager.get_situations_by_tags(cls.tags):
            situation_sims.update(situation.invited_sim_ids)
        blacklist_sim_ids = set(itertools.chain(situation_sims, instanced_sim_ids, household_sim_ids, auto_fill_blacklist))
        filter_results = services.sim_filter_service().submit_matching_filter(sim_filter=cls.group_filter, allow_yielding=False, blacklist_sim_ids=blacklist_sim_ids, gsi_source_fn=cls.get_sim_filter_gsi_name)
        if not filter_results:
            return
        for result in filter_results:
            job_role = cls.situation_job_mapping.get(result.tag, None)
            if job_role is None:
                continue
            guest_list.add_guest_info(SituationGuestInfo(result.sim_info.sim_id, job_role.situation_job, RequestSpawningOption.DONT_CARE, job_role.situation_job.sim_auto_invite_allow_priority))
        return guest_list

    @classmethod
    def default_job(cls):
        pass

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

    def _on_remove_sim_from_situation(self, sim):
        super()._on_remove_sim_from_situation(sim)
        if self.force_leave_on_exit:
            services.get_zone_situation_manager().make_sim_leave_now_must_run(sim)
Exemplo n.º 19
0
class LampoonPartySituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'bartender':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the Bartender.\n                        '
            ),
            bartender_party_role_state=RoleState.TunableReference(
                description=
                "\n                        Bartender's role state to prepare drinks, socialize, etc. during the party.\n                        "
            ),
            tuning_group=GroupNames.ROLES),
        'host':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the host.\n                        '
            ),
            host_party_role_state=RoleState.TunableReference(
                description=
                "\n                        The host's role state during the party.\n                        "
            ),
            tuning_group=GroupNames.ROLES),
        'entertainer':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the entertainer.\n                        '
            ),
            entertainer_party_role_state=RoleState.TunableReference(
                description=
                "\n                        Entertainer's role state during the party.\n                        "
            ),
            tuning_group=GroupNames.ROLES),
        'guest':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the Guests.\n                        '
            ),
            guest_party_role_state=RoleState.TunableReference(
                description=
                "\n                        Guest's role state during the party.\n                        "
            ),
            tuning_group=GroupNames.ROLES),
        'guest_of_honor':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the Guest of Honor.\n                        '
            ),
            guest_of_honor_party_role_state=RoleState.TunableReference(
                description=
                "\n                        Guest of Honor's role state during the party.\n                        "
            ),
            tuning_group=GroupNames.ROLES)
    }
    REMOVE_INSTANCE_TUNABLES = ('venue_invitation_message',
                                'venue_situation_player_job')

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.bartender.situation_job,
                 cls.bartender.bartender_party_role_state),
                (cls.host.situation_job, cls.host.host_party_role_state),
                (cls.entertainer.situation_job,
                 cls.entertainer.entertainer_party_role_state),
                (cls.guest.situation_job, cls.guest.guest_party_role_state),
                (cls.guest_of_honor.situation_job,
                 cls.guest_of_honor.guest_of_honor_party_role_state)]

    @classmethod
    def default_job(cls):
        return cls.guest.situation_job

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

    def start_situation(self):
        super().start_situation()
        self._change_state(_RoastState())
class OpenStreetsAutonomySituation(situations.situation_complex.SituationComplexCommon):
    __qualname__ = 'OpenStreetsAutonomySituation'
    INSTANCE_TUNABLES = {'role': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='\n                  The situation job for the sim.\n                  '), do_stuff_role_state=RoleState.TunableReference(description='\n                  The role state for the sim doing stuff.  This is the initial state.\n                  '), leave_role_state=RoleState.TunableReference(description='\n                  The role state for the sim leaving.\n                  ')), 'do_stuff_timeout': sims4.tuning.tunable.TunableSimMinute(description='\n            The amount of time the sim does stuff before leaving.\n            ', default=180), 'can_start_walkby_limiting_tags': TunableSet(description="\n                Don't start a situation of this type if another situation is\n                already running that has any of these tags in its tags field.\n                \n                For instance, if you only want one Streaker at a time you would\n                create a new tag SITUATION_STREAKER. Then set that in both this\n                field and in the tags field of situation_streaker.\n                ", tunable=TunableEnumWithFilter(tunable_type=Tag, default=Tag.INVALID, filter_prefixes=['situation']))}
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @staticmethod
    def _states():
        return [(1, _DoStuffState), (2, _LeaveState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.role.situation_job, cls.role.do_stuff_role_state)]

    @classmethod
    def default_job(cls):
        return cls.role.situation_job

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

    @classmethod
    def can_start_walkby(cls, lot_id):
        return not services.get_zone_situation_manager().is_situation_with_tags_running(cls.can_start_walkby_limiting_tags)

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.OPEN_STREETS
class InviteOverSituation(VisitingNPCSituation):
    __qualname__ = 'InviteOverSituation'
    INSTANCE_TUNABLES = {'invited_job': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='\n                          A reference to the SituationJob used for the Sim invited over.\n                          '), ring_doorbell_state=RoleState.TunableReference(description='\n                          The state for telling a Sim to try to ring the doorbell followed by inviting themselves in.\n                          ')), 'purpose': sims4.tuning.tunable.TunableEnumEntry(description='\n                The purpose/reason used to create the venue type specific visit situation,\n                after the invited sim attempts to ring the door bell.\n                This should be tuned to Invite In, but since that is a dynamic enum\n                you must do it yourself.\n                ', tunable_type=venues.venue_constants.NPCSummoningPurpose, default=venues.venue_constants.NPCSummoningPurpose.DEFAULT)}

    @staticmethod
    def _states():
        return [(1, _RingDoorBellState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.invited_job.situation_job, cls.invited_job.ring_doorbell_state)]

    @classmethod
    def default_job(cls):
        return cls.invited_job.situation_job

    @classproperty
    def supports_multiple_sims(cls):
        return False

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

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._invited_sim = sim

    def _on_sim_removed_from_situation_prematurely(self, sim):
        super()._on_sim_removed_from_situation_prematurely(sim)
        self._invited_sim = None

    def _invite_sim_in(self):
        if self._invited_sim is not None:
            services.current_zone().venue_service.venue.summon_npcs((self._invited_sim.sim_info,), self.purpose, self.initiating_sim_info)
        self._self_destruct()
Exemplo n.º 22
0
class HospitalPatientSituation(PatientSituationBase):
    INSTANCE_TUNABLES = {
        'admitted_role_state':
        RoleState.TunableReference(
            description=
            '\n            A reference to the hospital patients admitted\n            role state while in the situation. This is the\n            state where the patient is assigned to a bed \n            and the doctor is actively trying to diagnose\n            the issue.\n            ',
            tuning_group=PatientSituationBase.JOB_AND_STATE_GROUP,
            display_name='03_admitted_role_state'),
        'diagnosed_role_state':
        RoleState.TunableReference(
            description=
            '\n            A reference to the hospital patients diagnosed\n            role state while in the situation. This is\n            the state where the patient has been diagnosed \n            but it still waiting for the doctor to treat\n            them.\n            ',
            tuning_group=PatientSituationBase.JOB_AND_STATE_GROUP,
            display_name='04_diagnosed_role_state'),
        'go_to_diagnosed_interactions':
        TunableInteractionOfInterest(
            description=
            '\n            The interactions to look for when a Sim has been diagnosed by a \n            doctor and is now waiting for treatment.\n            ',
            tuning_group=PatientSituationBase.STATE_ADVANCEMENT_GROUP),
        'admitted_duration_for_time_jump':
        TunableSimMinute(
            description=
            '\n            The amount of time allowed to pass before a Sim in the admitted\n            state will be ignored on load with a time jump.\n            ',
            default=180,
            tuning_group=PatientSituationBase.TIMEOUT_GROUP),
        'diagnosed_duration_for_time_jump':
        TunableSimMinute(
            description=
            '\n            The amount of time allowed to pass before a Sim in the diagnosed\n            state will be ignored on load with a time jump.\n            ',
            default=180,
            tuning_group=PatientSituationBase.TIMEOUT_GROUP),
        'pre_diagnosed':
        Tunable(
            description=
            '\n            If this is true then when the Sim is pre-rolled it will skip to the\n            _DiagnosedState(). \n            \n            If it is False then it will default to pre-rolling\n            the Sim to _AdmittedState().\n            ',
            tunable_type=bool,
            default=False)
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(1, ArrivingState),
                SituationStateData(2, WaitingState),
                SituationStateData(3, _AdmittedState),
                SituationStateData(4, _DiagnosedState),
                SituationStateData(5, TreatedState))

    def _skip_ahead_for_preroll(self):
        if self.pre_diagnosed:
            self._change_state(_DiagnosedState())
        else:
            self._change_state(_AdmittedState())

    def _on_done_waiting(self):
        self._change_state(_AdmittedState())

    @classmethod
    def should_state_type_load_after_time_jump(cls, state_type):
        if not super().should_state_type_load_after_time_jump(state_type):
            return False
        else:
            elapsed_time = services.current_zone(
            ).time_elapsed_since_last_save().in_minutes()
            if state_type is _AdmittedState:
                timeout = cls.admitted_duration_for_time_jump
            elif state_type is _DiagnosedState:
                timeout = cls.diagnosed_duration_for_time_jump
            else:
                timeout = None
            if timeout is not None and elapsed_time >= timeout:
                return False
        return True
class WalkbyRingDoorBellSituation(SituationComplexCommon):
    __qualname__ = 'WalkbyRingDoorBellSituation'
    INSTANCE_TUNABLES = {
        'walker_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                          A reference to the SituationJob used for the Sim performing the walkby\n                          '
            ),
            ring_doorbell_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling a Sim to go and ring the doorbell.  This is the initial state.\n                          '
            ),
            mailbox_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling a Sim to go wait by the mailbox. \n                          This is a fall back for when they cannot reach the front door.\n                          '
            ),
            wait_for_invitation_state=RoleState.TunableReference(
                description=
                '\n                          The state for telling a Sim to wait for the other Sim to invite them in.\n                          '
            ),
            leave_state=RoleState.TunableReference(
                description=
                "\n                          The state for the sim leaving if you don't invite them in.\n                          "
            ),
            tuning_group=GroupNames.SITUATION),
        'wait_for_invitation_delay':
        sims4.tuning.tunable.TunableSimMinute(
            description=
            '\n                                        The amount of time to wait for a Sim to greet the walker Sim.',
            default=60,
            tuning_group=GroupNames.SITUATION),
        'can_start_walkby_limiting_tags':
        TunableSet(
            description=
            "\n                Don't start a situation of this type if another situation is\n                already running that has any of these tags in its tags field.\n                Basically we don't want two sims ringing your doorbell at the same\n                time because that would be weird.\n                ",
            tunable=TunableEnumWithFilter(tunable_type=Tag,
                                          default=Tag.INVALID,
                                          filter_prefixes=['situation']))
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_buff', '_cost', '_NPC_host_filter', '_NPC_hosted_player_tests',
        'NPC_hosted_situation_start_message',
        'NPC_hosted_situation_use_player_sim_as_filter_requester',
        'NPC_hosted_situation_player_job', 'venue_types',
        'venue_invitation_message', 'venue_situation_player_job', 'category',
        'main_goal', 'minor_goal_chains', '_level_data', '_display_name',
        'max_participants', '_initiating_sim_tests', 'targeted_situation',
        '_resident_job', '_icon', 'situation_description')

    @staticmethod
    def _states():
        return [(2, _RingDoorBellState), (3, _MailboxState),
                (4, _WaitForInvitationState), (5, _LeaveState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.walker_job.situation_job,
                 cls.walker_job.ring_doorbell_state)]

    @classmethod
    def default_job(cls):
        return cls.walker_job.situation_job

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._walker = None
        self._state_interruptible_by_user_action = True

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._walker = sim

    def _on_remove_sim_from_situation(self, sim):
        super()._on_remove_sim_from_situation(sim)
        self._walker = None

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    def _on_wait_for_invitation_expired(self):
        pass

    @classmethod
    def can_start_walkby(cls, lot_id):
        if services.get_zone_situation_manager(
        ).is_situation_with_tags_running(cls.can_start_walkby_limiting_tags):
            return False
        active_lot_id = services.active_household_lot_id()
        if active_lot_id is None:
            return False
        return lot_id == active_lot_id

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.OPEN_STREETS
Exemplo n.º 24
0
class WalkbyAmbientSituation(SituationComplexCommon):
    __qualname__ = 'WalkbyAmbientSituation'
    INSTANCE_TUNABLES = {
        'walker_job':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        A reference to the SituationJob used for the Sim performing the walkby\n                        '
            ),
            walkby_state=RoleState.TunableReference(
                description='The state while the sim is walking by.'),
            flavor_interaction_state=RoleState.TunableReference(
                description=
                '\n                            The role state when the sim does a flavor interaction.\n                            '
            ),
            tuning_group=GroupNames.SITUATION),
        'flavor_affordances':
        sims4.tuning.tunable.TunableList(
            description=
            '\n            When selected for walkby flavor the sim runs one of the affordances in\n            this list.\n            ',
            tunable=sims4.tuning.tunable.TunableReference(
                services.affordance_manager())),
        'flavor_cooldown':
        TunableSimMinute(
            description=
            '\n                The minimum amount of time from the end of one flavor action\n                until the walkby sim can perform another.\n                ',
            default=5,
            minimum=1,
            maximum=480),
        'flavor_chance_to_start':
        sims4.tuning.tunable.TunablePercent(
            description=
            '\n                This is the percentage chance that each walkby sim will start a flavor\n                interaction, such as using the phone, on an\n                ambient service ping. At most one will start per ping.\n                ',
            default=1),
        'can_start_walkby_limiting_tags':
        TunableSet(
            description=
            "\n                Don't start a situation of this type if another situation is\n                already running that has any of these tags in its tags field.\n                \n                For instance, if you only want one Streaker at a time you would\n                create a new tag SITUATION_STREAKER. Then set that in both this\n                field and in the tags field of situation_streaker.\n                ",
            tunable=TunableEnumWithFilter(tunable_type=Tag,
                                          default=Tag.INVALID,
                                          filter_prefixes=['situation']))
    }
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @staticmethod
    def _states():
        return [(1, _LeaveState), (2, _FlavorInteractionState),
                (3, _SocialState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.walker_job.situation_job, cls.walker_job.walkby_state)]

    @classmethod
    def default_job(cls):
        return cls.walker_job.situation_job

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._walker = None
        self._flavor_cooldown_until = services.time_service(
        ).sim_now + clock.interval_in_sim_minutes(10)
        self._social_cooldown_until = services.time_service(
        ).sim_now + clock.interval_in_sim_minutes(10)
        self._other_social_situation = None
        self._social_interaction = None

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

    def _save_custom_state(self, writer):
        uid = self._state_type_to_uid(_LeaveState)
        writer.write_uint32(
            situations.situation_complex.SituationComplexCommon.STATE_ID_KEY,
            uid)

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._walker = sim

    def _on_remove_sim_from_situation(self, sim):
        super()._on_remove_sim_from_situation(sim)
        self._walker = None

    def _on_sim_removed_from_situation_prematurely(self, sim):
        super()._on_sim_removed_from_situation_prematurely(sim)
        self.manager.add_sim_to_auto_fill_blacklist(sim.id)

    @classmethod
    def can_start_walkby(cls, lot_id):
        return not services.get_zone_situation_manager(
        ).is_situation_with_tags_running(cls.can_start_walkby_limiting_tags)

    @property
    def _should_cancel_leave_interaction_on_premature_removal(self):
        return True

    def get_sim_available_for_walkby_flavor(self):
        if self._cur_state is None or not self._cur_state._is_available_for_interruption(
        ):
            return
        if services.time_service().sim_now < self._flavor_cooldown_until:
            return
        if self._walker is not None and self._walker.opacity < 1.0:
            return
        if self._walker is not None and terrain.is_position_in_street(
                self._walker.position):
            return
        return self._walker

    def get_sim_available_for_social(self):
        if self._cur_state is None or not self._cur_state._is_available_for_interruption(
        ):
            return
        if services.time_service().sim_now < self._social_cooldown_until:
            return
        if self._walker is not None and self._walker.opacity < 1.0:
            return
        return self._walker

    def random_chance_to_start_flavor_interaction(self):
        return sims4.random.random_chance(self.flavor_chance_to_start * 100)

    def start_flavor_interaction(self):
        self._change_state(_FlavorInteractionState())

    def start_social(self, other_situation, social_interaction=None):
        self._other_social_situation = other_situation
        self._social_interaction = social_interaction
        self._change_state(_SocialState())

    def _on_flavor_finished(self):
        self._flavor_cooldown_until = services.time_service(
        ).sim_now + clock.interval_in_sim_minutes(self.flavor_cooldown)
        self._change_state(_LeaveState())

    def _on_social_finished(self):
        self._other_social_situation = None
        self._social_interaction = None
        self._social_cooldown_until = services.time_service(
        ).sim_now + clock.interval_in_sim_minutes(
            services.current_zone().ambient_service.SOCIAL_COOLDOWN)
        self._change_state(_LeaveState())

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.OPEN_STREETS
Exemplo n.º 25
0
class RetailEmployeeSituation(BusinessEmployeeSituationMixin,
                              SituationComplexCommon):
    class _EmployeeSituationState(HasTunableFactory, AutoFactoryInit,
                                  SituationState):
        FACTORY_TUNABLES = {
            'role_state':
            RoleState.TunableReference(
                description=
                '\n                The role state that is active on the employee for the duration\n                of this state.\n                '
            ),
            'timeout_min':
            TunableSimMinute(
                description=
                '\n                The minimum amount of time, in Sim minutes, the employee will be\n                in this state before moving on to a new state.\n                ',
                default=10),
            'timeout_max':
            TunableSimMinute(
                description=
                '\n                The maximum amount of time, in Sim minutes, the employee will be\n                in this state before moving on to a new state.\n                ',
                default=30),
            'push_interaction':
            TunableInteractionOfInterest(
                description=
                '\n                If an interaction of this type is run by the employee, this\n                state will activate.\n                '
            )
        }

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

        def on_activate(self, reader=None):
            super().on_activate(reader)
            self.owner._set_job_role_state(self.owner.employee_job,
                                           self.role_state)
            timeout = random.randint(self.timeout_min, self.timeout_max)
            self._create_or_load_alarm(self.state_name,
                                       timeout,
                                       self._timeout_expired,
                                       reader=reader)

        def _timeout_expired(self, *_, **__):
            self._change_state(self.owner._choose_next_state())

    INSTANCE_TUNABLES = {
        'employee_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for the employee.\n            '),
        'role_state_go_to_store':
        RoleState.TunableReference(
            description=
            '\n            The role state for getting the employee inside the store. This is\n            the default role state and will be run first before any other role\n            state can start.\n            '
        ),
        'role_state_go_to_store_timeout':
        TunableSimMinute(
            description=
            "\n            Automatically advance out of the role state after waiting for this\n            duration. There's a number of reasons the employee can fail to exit\n            the role state in a timely fashion, such as the register is blocked\n            (by another employee clocking, even) and hijacked by a social.\n            ",
            default=60),
        'state_socialize':
        _EmployeeSituationState.TunableFactory(
            description=
            '\n            The state during which employees socialize with customers.\n            ',
            locked_args={'state_name': 'socialize'}),
        'state_restock':
        _EmployeeSituationState.TunableFactory(
            description=
            '\n            The state during which employees restock items.\n            ',
            locked_args={'state_name': 'restock'}),
        'state_clean':
        _EmployeeSituationState.TunableFactory(
            description=
            '\n            The state during which employees clean the store.\n            ',
            locked_args={'state_name': 'clean'}),
        'state_slack_off':
        _EmployeeSituationState.TunableFactory(
            description=
            '\n            The state during which employees slack off.\n            ',
            locked_args={'state_name': 'slack_off'}),
        'state_ring_up_customers':
        _EmployeeSituationState.TunableFactory(
            description=
            '\n            The state during which employees will ring up customers.\n            ',
            locked_args={'state_name': 'ring_up_customers'}),
        'go_to_store_interaction':
        TunableInteractionOfInterest(
            description=
            '\n            The interaction that, when run by an employee, will switch the\n            situation state to start cleaning, upselling, restocking, etc.\n            '
        ),
        'go_home_interaction':
        TunableInteractionOfInterest(
            description=
            '\n            The interaction that, when run on an employee, will have them end\n            this situation and go home.\n            '
        )
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._employee_sim_info = None
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.state_socialize.push_interaction.custom_keys_gen())
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.state_restock.push_interaction.custom_keys_gen())
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.state_clean.push_interaction.custom_keys_gen())
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.state_slack_off.push_interaction.custom_keys_gen())
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.state_ring_up_customers.push_interaction.custom_keys_gen())
        self._register_test_event_for_keys(
            TestEvent.InteractionComplete,
            self.go_home_interaction.custom_keys_gen())

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _GoToStoreState),
                SituationStateData(2, cls.state_socialize),
                SituationStateData(5, cls.state_restock),
                SituationStateData(6, cls.state_clean),
                SituationStateData(7, cls.state_slack_off),
                SituationStateData(8, cls.state_ring_up_customers))

    @classmethod
    def _state_to_uid(cls, state_to_find):
        state_type_to_find = type(state_to_find)
        if state_type_to_find is _GoToStoreState:
            return 1
        state_name = getattr(state_to_find, 'state_name', None)
        if state_name is None:
            return cls.INVALID_STATE_UID
        for state_data in cls._states():
            if getattr(state_data.state_type, 'state_name',
                       None) == state_name:
                return state_data.uid
        return cls.INVALID_STATE_UID

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        writer.write_uint64('original_duration', self._original_duration)

    def handle_event(self, sim_info, event, resolver):
        if event == TestEvent.InteractionComplete:
            target_sim = resolver.interaction.get_participant(
                ParticipantType.TargetSim)
            if target_sim is None:
                target_sim = resolver.interaction.get_participant(
                    ParticipantType.Actor)
            target_sim = getattr(target_sim, 'sim_info', target_sim)
            if target_sim is self._employee_sim_info:
                if resolver(self.state_socialize.push_interaction):
                    self._change_state(self.state_socialize())
                elif resolver(self.state_restock.push_interaction):
                    self._change_state(self.state_restock())
                elif resolver(self.state_clean.push_interaction):
                    self._change_state(self.state_clean())
                elif resolver(self.state_slack_off.push_interaction):
                    self._change_state(self.state_slack_off())
                elif resolver(self.state_ring_up_customers.push_interaction):
                    self._change_state(self.state_ring_up_customers())
                elif resolver(self.go_home_interaction):
                    self._on_business_closed()
        super().handle_event(sim_info, event, resolver)

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.employee_job, cls.role_state_go_to_store)]

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

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

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.LOT

    def get_employee_sim_info(self):
        if self._employee_sim_info is not None:
            return self._employee_sim_info
        return next(self._guest_list.invited_sim_infos_gen(), None)

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._employee_sim_info = sim.sim_info
        self._update_work_buffs(from_load=True)

    @property
    def _is_clocked_in(self):
        business_manager = services.business_service(
        ).get_business_manager_for_zone()
        if business_manager is None:
            return False
        return business_manager.is_employee_clocked_in(self._employee_sim_info)

    def _choose_next_state(self):
        valid_states = [
            self.state_socialize, self.state_restock, self.state_clean,
            self.state_ring_up_customers
        ]
        random_state = random.choice(valid_states)
        return random_state()
class SituationComplexExperiment(SituationComplexCommon):
    __qualname__ = 'SituationComplexExperiment'
    INSTANCE_TUNABLES = {'test_job': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='A reference to a SituationJob that can be performed at this Situation.'), friendly_role_state=RoleState.TunableReference(description='A role state the sim assigned to the job will perform'), angry_role_state=RoleState.TunableReference(description='A role state the sim assigned to the job will perform')), '_host_job': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='A reference to a SituationJob that can be performed at this Situation.'), default_role_state=RoleState.TunableReference(description='A role state the sim assigned to the job will perform')), 'mean_test': event_testing.tests_with_data.TunableParticipantRanInteractionTest(locked_args={'participant': ParticipantType.TargetSim, 'interaction_outcome': None, 'running_time': None, 'tooltip': None}, description='Test for a mean interaction that will trigger a state change')}

    @staticmethod
    def _states():
        return [(1, AngrySituationState), (2, FriendlySituationState)]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        reader = self._seed.custom_init_params_reader
        if reader is None:
            self._test_bool = True
        else:
            self._test_bool = reader.read_bool('test_bool', True)

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.test_job.situation_job, cls.test_job.friendly_role_state), (cls._host_job.situation_job, cls._host_job.default_role_state)]

    @classmethod
    def default_job(cls):
        return cls.test_job.situation_job

    @classmethod
    def resident_job(cls):
        return cls._host_job.situation_job

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        writer.write_bool('test_bool', False)

    def start_situation(self):
        super().start_situation()
        self._change_state(FriendlySituationState())
class BirthdayPartySituation(SituationComplexCommon):
    __qualname__ = 'BirthdayPartySituation'
    INSTANCE_TUNABLES = {
        'celebrant':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the celebrant.'
            ),
            celebrant_gather_role_state=RoleState.TunableReference(
                description=
                "\n                        Celebrant's role state before the celebration (gather phase)."
            ),
            celebrant_reception_role_state=RoleState.TunableReference(
                description=
                "\n                        Celebrant's role state after the celebration (eat, drink, socialize, dance)."
            ),
            tuning_group=GroupNames.ROLES),
        'bartender':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the Bartender.'
            ),
            bartender_pre_reception_role_state=RoleState.TunableReference(
                description=
                "\n                        Bartender's role state to prepare drinks and socialize with guests."
            ),
            bartender_reception_role_state=RoleState.TunableReference(
                description=
                "\n                        Bartender's role state to prepare drinks, socialize, etc. during the reception."
            ),
            tuning_group=GroupNames.ROLES),
        'caterer':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the caterer.'),
            caterer_prep_role_state=RoleState.TunableReference(
                description=
                "\n                        Caterer's role state for preparing cake and meal for guests."
            ),
            caterer_serve_role_state=RoleState.TunableReference(
                description=
                "\n                        Caterer's role state for serving the guests."
            ),
            tuning_group=GroupNames.ROLES),
        'entertainer':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the entertainer.'
            ),
            entertainer_prep_reception_state=RoleState.TunableReference(
                description=
                "\n                        Entertainer's role state before reception."
            ),
            entertainer_reception_role_state=RoleState.TunableReference(
                description=
                "\n                        Entertainer's role state during reception."
            ),
            tuning_group=GroupNames.ROLES),
        'guest':
        sims4.tuning.tunable.TunableTuple(
            situation_job=SituationJob.TunableReference(
                description=
                '\n                        The SituationJob for the Guests.'),
            guest_gather_role_state=RoleState.TunableReference(
                description=
                "\n                        Guest's role state before the celebration (gather phase)."
            ),
            guest_gather_impatient_role_state=RoleState.TunableReference(
                description=
                "\n                        Guest's role state if it is taking too long for the celebration to start."
            ),
            guest_reception_role_state=RoleState.TunableReference(
                description=
                "\n                        Guest's role state after the celebration (now they can eat the cake)."
            ),
            tuning_group=GroupNames.ROLES),
        'start_reception':
        TunableInteractionOfInterest(
            description=
            '\n                        This is a birthday cake interaction where starting this interaction starts \n                        the cake reception phase.',
            tuning_group=GroupNames.TRIGGERS),
        'guests_become_impatient_timeout':
        TunableSimMinute(
            description=
            '\n                        If the celebration is not started in this amount of time the guests will grow impatient.',
            default=120,
            tuning_group=GroupNames.TRIGGERS)
    }
    REMOVE_INSTANCE_TUNABLES = (
        '_NPC_host_filter', '_NPC_hosted_player_tests',
        'NPC_hosted_situation_start_message',
        'NPC_hosted_situation_player_job',
        'NPC_hosted_situation_use_player_sim_as_filter_requester',
        'venue_invitation_message', 'venue_situation_player_job')

    @staticmethod
    def _states():
        return [(1, GatherState), (2, ImpatientGatherState),
                (3, ReceptionState)]

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.celebrant.situation_job,
                 cls.celebrant.celebrant_gather_role_state),
                (cls.bartender.situation_job,
                 cls.bartender.bartender_pre_reception_role_state),
                (cls.caterer.situation_job,
                 cls.caterer.caterer_prep_role_state),
                (cls.entertainer.situation_job,
                 cls.entertainer.entertainer_prep_reception_state),
                (cls.guest.situation_job, cls.guest.guest_gather_role_state)]

    @classmethod
    def default_job(cls):
        return cls.guest.situation_job

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

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        if job_type is self.celebrant.situation_job:
            self._celebrant_id = sim.sim_id

    def _is_birthday_starting(self, event, resolver):
        if event == TestEvent.InteractionStart and resolver(
                self.start_reception):
            participants = resolver.get_participants(ParticipantType.Actor)
            while True:
                for sim_info in participants:
                    while sim_info.id == self._celebrant_id:
                        return True
        return False
Exemplo n.º 28
0
class InviteToSituation(situations.situation_complex.SituationComplexCommon):
    INSTANCE_TUNABLES = {'invited_job': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='\n                          A reference to the SituationJob used for the Sims invited to.\n                          '), invited_to_state=RoleState.TunableReference(description='\n                          The state for telling a sim to wait. They will momentarily be\n                          pulled from this situation by a visit or venue situation.\n                          ')), 'purpose': sims4.tuning.tunable.TunableEnumEntry(description='\n                The purpose/reason used to perform the venue specific operation\n                to get this sim in the appropriate situation.\n                This should be tuned to Invite In, but since that is a dynamic enum\n                you must do it yourself.\n                ', tunable_type=venues.venue_constants.NPCSummoningPurpose, default=venues.venue_constants.NPCSummoningPurpose.DEFAULT)}
    REMOVE_INSTANCE_TUNABLES = Situation.SITUATION_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.invited_job.situation_job, cls.invited_job.invited_to_state)]

    @classmethod
    def default_job(cls):
        return cls.invited_job.situation_job

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

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

    def _issue_requests(self):
        pass

    def on_arrived(self):
        zone = services.current_zone()
        venue = zone.venue_service.active_venue
        for sim_info in self._seed.invited_sim_infos_gen():
            if sim_info.is_npc:
                venue.summon_npcs((sim_info,), self.purpose)

    @classmethod
    def get_player_greeted_status_from_seed(cls, situation_seed):
        for sim_info in situation_seed.invited_sim_infos_gen():
            if sim_info.is_npc:
                if sim_info.lives_here:
                    return GreetedStatus.GREETED
        return GreetedStatus.NOT_APPLICABLE
class RetailCustomerSituation(BusinessSituationMixin, SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'customer_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for the customer.\n            '),
        'role_state_go_to_store':
        RoleState.TunableReference(
            description=
            '\n            The role state for getting the customer inside the store. This is\n            the default role state and will be run first before any other role\n            state can start.\n            '
        ),
        'role_state_browse':
        OptionalTunable(
            description=
            '\n            If enabled, the customer will be able to browse items.\n            ',
            tunable=TunableTuple(
                role_state=RoleState.TunableReference(
                    description=
                    '\n                    The role state for the customer browsing items.\n                    '
                ),
                browse_time_min=TunableSimMinute(
                    description=
                    '\n                    The minimum amount of time, in sim minutes, the customer\n                    will browse before moving on to the next state. When the\n                    customer begins browsing, a random time will be chosen\n                    between the min and max browse time.\n                    ',
                    default=10),
                browse_time_max=TunableSimMinute(
                    description=
                    '\n                    The maximum amount of time, in sim minutes, the customer\n                    will browse before moving on to the next state. When the\n                    customer begins browsing, a random time will be chosen\n                    between the min and max browse time.\n                    ',
                    default=20),
                browse_time_extension_tunables=OptionalTunable(
                    TunableTuple(
                        description=
                        '\n                    A set of tunables related to browse time extensions.\n                    ',
                        extension_perk=TunableReference(
                            description=
                            '\n                        Reference to a perk that, if unlocked, will increase\n                        browse time by a set amount.\n                        ',
                            manager=services.get_instance_manager(
                                sims4.resources.Types.BUCKS_PERK)),
                        time_extension=TunableSimMinute(
                            description=
                            '\n                        The amount of time, in Sim minutes, that browse time\n                        will be increased by if the specified "extension_perk"\n                        is unlocked.\n                        ',
                            default=30))))),
        'role_state_buy':
        OptionalTunable(
            description=
            '\n            If enabled, the customer will be able to buy items.\n            ',
            tunable=TunableTuple(
                role_state=RoleState.TunableReference(
                    description=
                    '\n                    The role state for the customer buying items.\n                    '
                ),
                price_range=TunableInterval(
                    description=
                    '\n                    The minimum and maximum price of items this customer will\n                    buy.\n                    ',
                    tunable_type=int,
                    default_lower=1,
                    default_upper=100,
                    minimum=1))),
        'role_state_loiter':
        RoleState.TunableReference(
            description=
            '\n            The role state for the customer loitering. If Buy Role State and\n            Browse Role State are both disabled, the Sim will fall back to\n            loitering until Total Shop Time runs out.\n            '
        ),
        'go_to_store_interaction':
        TunableInteractionOfInterest(
            description=
            '\n            The interaction that, when run by a customer, will switch the\n            situation state to start browsing, buying, or loitering.\n            '
        ),
        'total_shop_time_max':
        TunableSimMinute(
            description=
            "\n            The maximum amount of time, in sim minutes, a customer will shop.\n            This time starts when they enter the store. At the end of this\n            time, they'll finish up whatever their current interaction is and\n            leave.\n            ",
            default=30),
        'total_shop_time_min':
        TunableSimMinute(
            description=
            "\n            The minimum amount of time, in sim minutes, a customer will shop.\n            This time starts when they enter the store. At the end of this\n            time, they'll finish up whatever their current interaction is and\n            leave.\n            ",
            default=1),
        'buy_interaction':
        TunableInteractionOfInterest(
            description=
            '\n            The interaction that, when run by a customer, buys an object.\n            '
        ),
        'initial_purchase_intent':
        TunableInterval(
            description=
            "\n            The customer's purchase intent statistic is initialized to a random\n            value in this interval when they enter the store.\n            ",
            tunable_type=int,
            default_lower=0,
            default_upper=100),
        'purchase_intent_extension_tunables':
        OptionalTunable(
            TunableTuple(
                description=
                '\n            A set of tunables related to purchase intent extensions.\n            ',
                extension_perk=TunableReference(
                    description=
                    '\n                Reference to a perk that, if unlocked, will increase purchase\n                intent by a set amount.\n                ',
                    manager=services.get_instance_manager(
                        sims4.resources.Types.BUCKS_PERK)),
                purchase_intent_extension=TunableRange(
                    description=
                    '\n                The amount to increase the base purchase intent statistic by if\n                the specified "extension_perk" is unlocked.\n                ',
                    tunable_type=int,
                    default=5,
                    minimum=0,
                    maximum=100))),
        'purchase_intent_empty_notification':
        TunableUiDialogNotificationSnippet(
            description=
            '\n            Notification shown by customer when purchase intent hits bottom and\n            the customer leaves.\n            '
        ),
        'nothing_in_price_range_notification':
        TunableUiDialogNotificationSnippet(
            description=
            "\n            Notification shown by customers who are ready to buy but can't find\n            anything in their price range.\n            "
        ),
        '_situation_start_tests':
        TunableCustomerSituationInitiationSet(
            description=
            '\n            A set of tests that will be run when determining if this situation\n            can be chosen to start. \n            '
        )
    }
    CONTINUE_SHOPPING_THRESHOLD = TunableSimMinute(
        description=
        "\n        If the customer has this much time or more left in their total shop\n        time, they'll start the browse/buy process over again after purchasing\n        something. If they don't have this much time remaining, they'll quit\n        shopping.\n        ",
        default=30)
    PRICE_RANGE = TunableTuple(
        description=
        '\n        Statistics that are set to the min and max price range statistics.\n        These are automatically added to the customer in this situation and\n        will be updated accordingly.\n        \n        The stats should not be persisted -- the situation will readd them\n        on load.\n        ',
        min=Statistic.TunablePackSafeReference(),
        max=Statistic.TunablePackSafeReference())
    PURCHASE_INTENT_STATISTIC = Statistic.TunablePackSafeReference(
        description=
        "\n        A statistic added to customers that track their intent to purchase\n        something. At the minimum value they will leave, and at max value they\n        will immediately try to buy something. Somewhere in between, there's a\n        chance for them to not buy something when they go to the buy state.\n        "
    )
    PURCHASE_INTENT_CHANCE_CURVE = TunableCurve(
        description=
        '\n        A mapping of Purchase Intent Statistic value to the chance (0-1) that\n        the customer will buy something during the buy state.\n        ',
        x_axis_name='Purchase Intent',
        y_axis_name='Chance')
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def can_start_situation(cls, resolver):
        return cls._situation_start_tests.run_tests(resolver)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._customer = None
        self._showing_purchase_intent = False
        reader = self._seed.custom_init_params_reader
        if reader is None:
            self._saved_purchase_intent = None
        else:
            self._saved_purchase_intent = reader.read_int64(
                'purchase_intent', None)
        self._min_price_range_multiplier = 1
        self._max_price_range_multiplier = 1
        self._total_shop_time_multiplier = 1
        self._purchase_intent_watcher_handle = None

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        if self._customer is not None:
            purchase_intent = self._customer.get_stat_value(
                self.PURCHASE_INTENT_STATISTIC)
            writer.write_int64('purchase_intent', int(purchase_intent))

    @classmethod
    def _states(cls):
        return (SituationStateData(1, _GoToStoreState),
                SituationStateData(2, _BrowseState),
                SituationStateData(3, _BuyState),
                SituationStateData(4, _LoiterState))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.customer_job, cls.role_state_go_to_store)]

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

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

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1

    @classproperty
    def situation_serialization_option(cls):
        return situations.situation_types.SituationSerializationOption.LOT

    def validate_customer(self, sim_info):
        if self._customer is None:
            return False
        return self._customer.sim_info is sim_info

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._customer = sim
        self._update_price_range_statistics()
        self._initialize_purchase_intent()

    def _on_remove_sim_from_situation(self, sim):
        sim_job = self.get_current_job_for_sim(sim)
        super()._on_remove_sim_from_situation(sim)
        self._remove_purchase_intent()
        self._customer = None
        services.get_zone_situation_manager().add_sim_to_auto_fill_blacklist(
            sim.id, sim_job)
        self._self_destruct()

    def _situation_timed_out(self, *args, **kwargs):
        if not isinstance(self._cur_state, _BuyState):
            super()._situation_timed_out(*args, **kwargs)

    def adjust_browse_time(self, multiplier):
        if type(self._cur_state) is _BrowseState:
            self._cur_state.adjust_timeout(multiplier)

    def adjust_total_shop_time(self, multiplier):
        if multiplier == 0:
            self._self_destruct()
        elif type(self._cur_state) is _GoToStoreState:
            self._total_shop_time_multiplier *= multiplier
        else:
            remaining_minutes = self._get_remaining_time_in_minutes()
            remaining_minutes *= multiplier
            self.change_duration(remaining_minutes)

    def adjust_price_range(self, min_multiplier=1, max_multiplier=1):
        if self.role_state_buy is None:
            return
        self._min_price_range_multiplier *= min_multiplier
        self._max_price_range_multiplier *= max_multiplier
        self._update_price_range_statistics()

    def _update_price_range_statistics(self):
        (min_price, max_price) = self._get_min_max_price_range()
        if self.PRICE_RANGE.min is not None:
            min_stat = self._customer.get_statistic(self.PRICE_RANGE.min)
            min_stat.set_value(min_price)
        if self.PRICE_RANGE.max is not None:
            max_stat = self._customer.get_statistic(self.PRICE_RANGE.max)
            max_stat.set_value(max_price)

    def _get_min_max_price_range(self):
        price_range = self.role_state_buy.price_range
        return (max(0, price_range.lower_bound *
                    self._min_price_range_multiplier),
                max(1, price_range.upper_bound *
                    self._max_price_range_multiplier))

    def _initialize_purchase_intent(self):
        if self.role_state_buy is None:
            return
        if self._saved_purchase_intent is None:
            purchase_intent = random.randint(
                self.initial_purchase_intent.lower_bound,
                self.initial_purchase_intent.upper_bound)
            if self.purchase_intent_extension_tunables is not None:
                active_household = services.active_household()
                if active_household is not None:
                    if active_household.bucks_tracker.is_perk_unlocked(
                            self.purchase_intent_extension_tunables.
                            extension_perk):
                        purchase_intent += self.purchase_intent_extension_tunables.purchase_intent_extension
            purchase_intent = sims4.math.clamp(
                self.PURCHASE_INTENT_STATISTIC.min_value + 1, purchase_intent,
                self.PURCHASE_INTENT_STATISTIC.max_value - 1)
        else:
            purchase_intent = self._saved_purchase_intent
        tracker = self._customer.get_tracker(self.PURCHASE_INTENT_STATISTIC)
        tracker.set_value(self.PURCHASE_INTENT_STATISTIC,
                          purchase_intent,
                          add=True)
        self._purchase_intent_watcher_handle = tracker.add_watcher(
            self._purchase_intent_watcher)
        if self._on_social_group_changed not in self._customer.on_social_group_changed:
            self._customer.on_social_group_changed.append(
                self._on_social_group_changed)

    def _remove_purchase_intent(self):
        if self._customer is not None:
            if self._purchase_intent_watcher_handle is not None:
                tracker = self._customer.get_tracker(
                    self.PURCHASE_INTENT_STATISTIC)
                tracker.remove_watcher(self._purchase_intent_watcher_handle)
                self._purchase_intent_watcher_handle = None
                tracker.remove_statistic(self.PURCHASE_INTENT_STATISTIC)
            if self._on_social_group_changed in self._customer.on_social_group_changed:
                self._customer.on_social_group_changed.remove(
                    self._on_social_group_changed)
            self._set_purchase_intent_visibility(False)

    def _on_social_group_changed(self, sim, group):
        if self._customer in group:
            if self._on_social_group_members_changed not in group.on_group_changed:
                group.on_group_changed.append(
                    self._on_social_group_members_changed)
        elif self._on_social_group_members_changed in group.on_group_changed:
            group.on_group_changed.remove(
                self._on_social_group_members_changed)

    def _on_social_group_members_changed(self, group):
        if self._customer is not None:
            employee_still_in_group = False
            business_manager = services.business_service(
            ).get_business_manager_for_zone()
            if self._customer in group:
                for sim in group:
                    if not business_manager.is_household_owner(
                            sim.household_id):
                        if business_manager.is_employee(sim.sim_info):
                            employee_still_in_group = True
                            break
                    employee_still_in_group = True
                    break
            if employee_still_in_group:
                self._set_purchase_intent_visibility(True)
            else:
                self._set_purchase_intent_visibility(False)

    def on_sim_reset(self, sim):
        super().on_sim_reset(sim)
        if isinstance(self._cur_state, _BuyState) and self._customer is sim:
            new_buy_state = _BuyState()
            new_buy_state.object_id = self._cur_state.object_id
            self._change_state(new_buy_state)

    def _set_purchase_intent_visibility(self, toggle):
        if self._showing_purchase_intent is not toggle and (
                not toggle or isinstance(self._cur_state, _BrowseState)):
            self._showing_purchase_intent = toggle
            stat = self._customer.get_statistic(self.PURCHASE_INTENT_STATISTIC,
                                                add=False)
            if stat is not None:
                value = stat.get_value()
                self._send_purchase_intent_message(stat.stat_type, value,
                                                   value, toggle)

    def _purchase_intent_watcher(self, stat_type, old_value, new_value):
        if stat_type is not self.PURCHASE_INTENT_STATISTIC:
            return
        self._send_purchase_intent_message(stat_type, old_value, new_value,
                                           self._showing_purchase_intent)
        if new_value == self.PURCHASE_INTENT_STATISTIC.max_value:
            self._on_purchase_intent_max()
        elif new_value == self.PURCHASE_INTENT_STATISTIC.min_value:
            self._on_purchase_intent_min()

    def _send_purchase_intent_message(self, stat_type, old_value, new_value,
                                      toggle):
        business_manager = services.business_service(
        ).get_business_manager_for_zone()
        if business_manager is not None and business_manager.is_owner_household_active:
            op = PurchaseIntentUpdate(
                self._customer.sim_id,
                stat_type.convert_to_normalized_value(old_value),
                stat_type.convert_to_normalized_value(new_value), toggle)
            distributor.system.Distributor.instance().add_op(
                self._customer, op)

    def _on_purchase_intent_max(self):
        if isinstance(self._cur_state, _BuyState):
            return
        if isinstance(self._cur_state, _GoToStoreState):
            self._set_shop_duration()
        self._change_state(_BuyState())

    def _on_purchase_intent_min(self):
        resolver = SingleSimResolver(self._customer)
        dialog = self.purchase_intent_empty_notification(
            self._customer, resolver)
        dialog.show_dialog()
        self._self_destruct()

    def _choose_starting_state(self):
        if self.role_state_browse is not None:
            return _BrowseState()
        if self.role_state_buy is not None:
            return _BuyState()
        return _LoiterState()

    def _choose_post_browse_state(self):
        if self._customer is None:
            return
        if self.role_state_buy is not None:
            stat = self._customer.get_statistic(self.PURCHASE_INTENT_STATISTIC,
                                                add=False)
            if stat is not None:
                value = stat.get_value()
                chance = self.PURCHASE_INTENT_CHANCE_CURVE.get(value)
                if random.random() > chance:
                    return _BrowseState()
            self._set_purchase_intent_visibility(False)
            return _BuyState()
        return _LoiterState()

    def _choose_post_buy_state(self):
        minutes_remaining = self._get_remaining_time_in_minutes()
        if minutes_remaining < self.CONTINUE_SHOPPING_THRESHOLD:
            return
        if self.role_state_browse is not None:
            return _BrowseState()
        return _LoiterState()

    def _set_shop_duration(self):
        shop_time = random.randint(self.total_shop_time_min,
                                   self.total_shop_time_max)
        shop_time *= self._total_shop_time_multiplier
        self.change_duration(shop_time)
Exemplo n.º 30
0
class CareerEventSituation(SituationComplexCommon):

    class _SituationTravelRequestCareerEvent(_SituationTravelRequestDisallow):

        def __call__(self, user_facing_situation, travel_situation_type, travel_request_fn, is_career_event=False, **kwargs):
            if is_career_event:
                return travel_request_fn()
            if self.dialog is not None:
                dialog = self.dialog(user_facing_situation._sim, SingleSimResolver(user_facing_situation._sim))
                dialog.show_dialog()

        @property
        def restrict(self):
            return SituationTravelRequestType.CAREER_EVENT

    INSTANCE_TUNABLES = {'user_job': TunableTuple(description='\n            The job and role which the career Sim is placed into.\n            ', situation_job=SituationJob.TunableReference(description='\n                A reference to a SituationJob that can be performed at this Situation.\n                '), role_state=RoleState.TunableReference(description='\n                A role state the sim assigned to the job will perform.\n                ')), 'build_buy_lock_reason': OptionalTunable(description='\n            If enabled then build buy will be disabled during this situation.\n            ', tunable=TunableLocalizedString(description='\n                The reason build buy is locked. For this case, it is because \n                build buy is not allowed during active career. Used on the disabled \n                buildbuy button in the HUD.\n                ')), 'travel_request_behavior': _SituationTravelRequestCareerEvent.TunableFactory(), 'user_facing_type_override': OptionalTunable(description="\n            If enabled, sets and override for the type of user facing career\n            event this is. If this is disabled, it'll be defaulted to\n            CAREER_EVENT. This is mostly used by UI and in general should not be\n            necessary. In some cases, like Acting, we want to indicate to UI\n            that this is different from other career events.\n            ", tunable=TunableEnumEntry(description='\n                The situation event type to override this to.\n                ', tunable_type=SituationUserFacingType, default=SituationUserFacingType.CAREER_EVENT, invalid_enums=(SituationUserFacingType.CAREER_EVENT,))), 'destroy_situations_on_remove': TunableList(description='\n            Other situations to destroy when this situation is being destroyed.\n            ', tunable=TunableReference(manager=services.get_instance_manager(sims4.resources.Types.SITUATION)))}
    REMOVE_INSTANCE_TUNABLES = ('_resident_job', 'recommended_job_object_notification', 'recommended_job_object_text', 'force_invite_only', 'duration', 'targeted_situation', '_relationship_between_job_members', '_implies_greeted_status', '_survives_active_household_change') + Situation.SITUATION_START_FROM_UI_REMOVE_INSTANCE_TUNABLES

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._sim = None
        self._career_session_extended = False
        reader = self._seed.custom_init_params_reader
        if reader is not None:
            self._career_session_extended = reader.read_bool(CAREER_SESSION_EXTENDED, False)

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls.user_job.situation_job, cls.user_job.role_state)]

    @classmethod
    def default_job(cls):
        return cls.user_job.situation_job

    def _send_build_buy_lock(self):
        if self.build_buy_lock_reason is not None:
            op = BuildBuyLockUnlock(True, self.build_buy_lock_reason)
            distributor.system.Distributor.instance().add_op_with_no_owner(op)

    def start_situation(self):
        super().start_situation()
        self._change_state(CareerEventSituationState())
        self._send_build_buy_lock()

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

    def on_remove(self):
        super().on_remove()
        if self.build_buy_lock_reason is not None:
            op = BuildBuyLockUnlock(False)
            distributor.system.Distributor.instance().add_op_with_no_owner(op)
        situation_manager = services.get_zone_situation_manager()
        if situation_manager is not None:
            for situation_type in self.destroy_situations_on_remove:
                for situation_to_destroy in situation_manager.get_situations_by_type(situation_type):
                    if situation_to_destroy is not None:
                        situation_manager.destroy_situation_by_id(situation_to_destroy.id)

    @property
    def user_facing_type(self):
        if self.user_facing_type_override:
            return self.user_facing_type_override
        return SituationUserFacingType.CAREER_EVENT

    def build_situation_start_message(self):
        msg = super().build_situation_start_message()
        msg.is_active_career = True
        msg.has_stayed_late = self._career_session_extended
        return msg

    def build_situation_duration_change_op(self):
        msg = super().build_situation_duration_change_op()
        msg.has_stayed_late = self._career_session_extended
        return msg

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._sim = sim

    def _save_custom_situation(self, writer):
        super()._save_custom_situation(writer)
        writer.write_bool(CAREER_SESSION_EXTENDED, self._career_session_extended)

    def get_situation_goal_actor(self):
        return self._sim.sim_info

    def on_career_session_extended(self, new_duration):
        self._career_session_extended = True
        self.change_duration(new_duration)