예제 #1
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
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())
예제 #3
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 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 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()
예제 #6
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
예제 #7
0
class AnchoredOpenStreetsAutonomySituation(WalkbyLimitingTagsMixin, GroupAnchoredAutonomySituationCommon):
    INSTANCE_TUNABLES = {'role': sims4.tuning.tunable.TunableTuple(situation_job=SituationJob.TunableReference(description='\n                          The situation job for all sims in this situation.\n                          '), arriving_role_state=RoleState.TunableReference(description='\n                          The role state for the sim arriving on the spawn point and waiting \n                          for the rest of the group.  This is the initial state.\n                          '), do_stuff_role_state=RoleState.TunableReference(description='\n                          The role state for the sim doing stuff.\n                          '), leave_role_state=RoleState.TunableReference(description='\n                          The role state for the sim leaving.\n                          '), tuning_group=GroupNames.ROLES), 'group_filter': sims4.tuning.tunable.TunableReference(description="\n                                The group filter for this walkby.  If set, this filter will be used \n                                instead of the filter tuned in the walker_job.  If it's None, the \n                                filter in the walker_job will be used.  Note that all sims spawned \n                                with this filter will be put into the walker_job job.\n                            ", manager=services.get_instance_manager(sims4.resources.Types.SIM_FILTER), class_restrictions=filters.tunable.TunableAggregateFilter, tuning_group=GroupNames.ROLES), 'wait_for_arrival_timeout': sims4.tuning.tunable.TunableSimMinute(description='\n                                        The amount of time the sim waits at the spawn point before doing\n                                        stuff.\n                                        ', default=30, tuning_group=GroupNames.TRIGGERS), 'do_stuff_timeout': sims4.tuning.tunable.TunableSimMinute(description='\n                                    The amount of time the sim does stuff before leaving.\n                                    ', default=180, tuning_group=GroupNames.TRIGGERS)}
    REMOVE_INSTANCE_TUNABLES = situations.situation.Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

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

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

    @property
    def guests(self):
        return self._guests

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

        def not_on_active_lot(obj):
            return not obj.is_on_active_lot()

        self._anchor_position = self.get_new_anchor_position(self.object_anchor_tag, test_func=not_on_active_lot)
        self._change_state(_ArrivingState())

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        if self._cur_state.transition_to_doing_stuff_when_full() and (self.group_filter is None or len(self._guests) >= self.group_filter.get_filter_count()):
            self._change_state(_DoStuffState())

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

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

    @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()
예제 #8
0
class GymVisitorSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'situation_default_job':
        SituationJob.TunableReference(
            description=
            '\n            The default job that a visitor will be in during the situation.\n            '
        ),
        'exercise_state':
        _ExerciseState.TunableFactory(
            description=
            '\n            The main state of the situation. This is where Sims will do \n            everything except for arrive and leave.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='01_exercise_state'),
        'take_shower_state':
        _TakeShowerState.TunableFactory(
            description=
            '\n            The main state of the situation. This is where Sims will do \n            everything except for arrive and leave.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='02_take_shower_state'),
        'do_stuff_state':
        _DoStuffState.TunableFactory(
            description=
            '\n            The main state of the situation. This is where Sims will do \n            everything except for arrive and leave.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='03_do_stuff_state')
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

    @classmethod
    def _states(cls):
        return [
            SituationStateData(1, _ExerciseState, factory=cls.exercise_state),
            SituationStateData(2,
                               _TakeShowerState,
                               factory=cls.take_shower_state),
            SituationStateData(3, _DoStuffState, factory=cls.do_stuff_state)
        ]

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

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return list(
            cls.exercise_state._tuned_values.job_and_role_changes.items())
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
예제 #11
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())
예제 #12
0
class RelaxationCenterVisitorSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'situation_default_job': SituationJob.TunableReference(description='\n            The default job that a visitor will be in during the situation.\n            '), 'arriving_situation_state': _ArrivingState.TunableFactory(description='\n            The situation state used for when a Sim is arriving as a Massage \n            Therapist.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='01_arriving_situation_state'), 'do_stuff_situation_state': _DoStuffState.TunableFactory(description='\n            The main state of the situation. This is where Sims will do \n            everything except for arrive and leave.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='02_do_stuff_situation_state'), 'change_clothes_leave_situation_state': _ChangeClothesLeave.TunableFactory(description='\n            The state that is used to get the Sim to change clothes before \n            ending the situation and ending up in the leave lot situation.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='03_change_clothes_leave_situation_state')}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._visitor = None
        reader = self._seed.custom_init_params_reader
        self.apply_situation_outfit = False
        if reader is not None:
            self.apply_situation_outfit = reader.read_bool(CUSTOM_SAVE_OUTFIT, False)

    @classmethod
    def _states(cls):
        return [SituationStateData(1, _ArrivingState, factory=cls.arriving_situation_state), SituationStateData(2, _DoStuffState, factory=cls.do_stuff_situation_state), SituationStateData(3, _ChangeClothesLeave, factory=cls.change_clothes_leave_situation_state)]

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

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

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._visitor = sim
        if self.apply_situation_outfit:
            self.set_job_uniform(sim, job_type, (OutfitCategory.SITUATION, 0))
            sim.sim_info.try_set_current_outfit((OutfitCategory.SITUATION, 0))

    def _save_custom_situation(self, writer):
        if self._visitor is not None:
            writer.write_bool(CUSTOM_SAVE_OUTFIT, self._visitor.get_current_outfit()[0] == OutfitCategory.SITUATION)

    def sim_of_interest(self, sim_info):
        if self._visitor is not None and self._visitor.sim_info is sim_info:
            return True
        return False

    def all_sims_of_interest_arrived(self, arrived_sims):
        return True
예제 #13
0
class AnchoredRelaxationCenterVisitorSituation(GroupAnchoredAutonomySituationCommon):
    INSTANCE_TUNABLES = {'situation_default_job': SituationJob.TunableReference(description='\n            The default job that a visitor will be in during the situation.\n            '), 'arriving_situation_state': _ArrivingState.TunableFactory(description='\n            The situation state used for when a Sim is arriving as a Massage \n            Therapist.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='01_arriving_situation_state'), 'do_stuff_situation_state': _DoStuffState.TunableFactory(description='\n            The main state of the situation. This is where Sims will do \n            everything except for arrive and leave.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='02_do_stuff_situation_state'), 'change_clothes_leave_situation_state': _ChangeClothesLeave.TunableFactory(description='\n            The state that is used to get the Sim to change clothes before \n            ending the situation and ending up in the leave lot situation.\n            ', tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP, display_name='03_change_clothes_leave_situation_state')}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return [SituationStateData(1, _ArrivingState, factory=cls.arriving_situation_state), SituationStateData(2, _DoStuffState, factory=cls.do_stuff_situation_state), SituationStateData(3, _ChangeClothesLeave, factory=cls.change_clothes_leave_situation_state)]

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

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

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

    def start_situation(self):
        super().start_situation()
        self._anchor_position = self.get_new_anchor_position(self.object_anchor_tag)
        self._change_state(self.arriving_situation_state())

    def sim_of_interest(self, sim_info):
        for sim in self._situation_sims:
            if sim.sim_info is sim_info:
                return True
        return False

    def all_sims_of_interest_arrived(self, arrived_sims):
        for sim in self._situation_sims:
            if sim.sim_info not in arrived_sims:
                return False
        return True
예제 #14
0
class HostSituation(BusinessEmployeeSituationMixin,
                    StaffedObjectSituationMixin, SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'situation_job':
        SituationJob.TunableReference(
            description=
            '\n            The job that a staff member will be in during the situation.\n            '
        ),
        '_arriving_situation_state':
        _ArrivingState.TunableFactory(
            description=
            '\n            The situation state used for when a Sim is arriving as a staff \n            member.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='01_arriving_situation_state'),
        '_host_station_situation_state':
        _HostStationState.TunableFactory(
            description=
            '\n            The situation state used for when a Host is idling at the host station.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='02_host_station_situation_state'),
        '_show_to_table_situation_state':
        _ShowToTableState.TunableFactory(
            description=
            '\n            The situation state used for when a Host is showing a group to\n            their table.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='03_show_to_table_situation_state'),
        '_right_this_way_situation_state':
        _RightThisWayState.TunableFactory(
            description=
            '\n            The situation state used to get the Host to run the "Right this way" \n            social before routing to the table with the guests.\n            ',
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP,
            display_name='04_right_this_way_situation_state'),
        'right_this_way_interaction':
        TunableReference(
            description=
            '\n            The interaction that the Host runs before both the Host and the\n            guests start routing to the table.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION),
            class_restrictions='SuperInteraction'),
        'host_show_to_table_interaction':
        TunablePackSafeReference(
            description=
            '\n            The interaction to push on the host that will "show" the guests\n            to their table.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION)),
        'requesting_sim_show_to_table_interaction':
        TunablePackSafeReference(
            description=
            '\n            The interaction to push on the sim requesting a table that will \n            get the guest close to their table so a social can be pushed on them\n            to show them the table.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION)),
        'guest_take_seat_interaction':
        TunablePackSafeReference(
            description=
            '\n            The interaction to push on each of the guests in the group that\n            targets their chosen seat.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.INTERACTION)),
        'host_head_start_in_sim_minutes':
        Tunable(
            description=
            '\n            The number of sim minutes that the guests will wait after the host\n            begins to show them to their table before they start to follow.\n            ',
            tunable_type=int,
            default=1)
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

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

    @classmethod
    def _states(cls):
        return [
            SituationStateData(1,
                               _ArrivingState,
                               factory=cls._arriving_situation_state),
            SituationStateData(2,
                               _HostStationState,
                               factory=cls._host_station_situation_state),
            SituationStateData(3,
                               _ShowToTableState,
                               factory=cls._show_to_table_situation_state),
            SituationStateData(4,
                               _RightThisWayState,
                               factory=cls._right_this_way_situation_state)
        ]

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

    @classmethod
    def default_job(cls):
        pass

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._start_work_duration()

    def get_group_to_seat(self):
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            return
        return zone_director.get_next_group_id_to_seat()

    def remove_group_to_seat(self, group_id):
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            return
        return zone_director.remove_group_waiting_to_be_seated(group_id)

    def push_right_this_way_social(self, group):
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            return False
        else:
            host = self.get_staff_member()
            target = group.get_main_sim()
            if target is None:
                target = next(group.all_sims_in_situation_gen())
            context = InteractionContext(host,
                                         InteractionContext.SOURCE_SCRIPT,
                                         Priority.High)
            if not host.push_super_affordance(self.right_this_way_interaction,
                                              target, context):
                return False
        return True

    def push_show_table_interactions(self, group):
        if self.host_show_to_table_interaction is None or self.guest_take_seat_interaction is None:
            log_host_action(
                'During Show To Table State',
                'Failed - No Interactions to push on one of the Sims.')
            return False
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            log_host_action('During Show To Table State',
                            "Failed - Can't find the zone director.")
            return False
        master_sim = group.get_main_sim()
        if master_sim is None:
            master_sim = next(group.all_sims_in_situation_gen())
        zone_director.claim_table(master_sim)
        return self.push_host_show_table_interaction(group, zone_director,
                                                     master_sim)

    def push_host_show_table_interaction(self, group, zone_director,
                                         master_sim):
        host = self.get_staff_member()
        group_tables = zone_director.get_tables_by_group_id(group.id)
        if not group_tables:
            log_host_action(
                'During Show To Table',
                'Failed to push show to table interactions. Unable to find tables the group reserved.'
            )
            return False
        context = InteractionContext(host, InteractionContext.SOURCE_SCRIPT,
                                     Priority.High)
        master_sim_context = InteractionContext(
            master_sim, InteractionContext.SOURCE_SCRIPT, Priority.High)
        object_manager = services.current_zone().object_manager
        table_id = group_tables.pop()
        table = object_manager.get(table_id)
        (master_sim_chair_id, _) = zone_director.get_sims_seat(master_sim)
        master_sim_chair = object_manager.get(master_sim_chair_id)
        if not host.push_super_affordance(
                self.host_show_to_table_interaction,
                table,
                context,
                saved_participants=[master_sim, master_sim_chair, None, None]):
            log_host_action(
                'During Show To Table State',
                'Failed to Push Host Interaction on {}'.format(host))
            return False
        log_host_action('During Show To Table State',
                        'Pushed Host Interaction on {}'.format(host))

        def timer_expired(handle):
            self.push_master_sim_interaction(group, table, master_sim,
                                             master_sim_context, host,
                                             master_sim_chair)
            self._alarm_handle = None

        self._alarm_handle = alarms.add_alarm(
            self,
            clock.interval_in_sim_minutes(self.host_head_start_in_sim_minutes),
            timer_expired)
        return True

    def push_master_sim_interaction(self, group, table, master_sim,
                                    master_sim_context, host,
                                    master_sim_chair):
        if not master_sim.push_super_affordance(
                self.requesting_sim_show_to_table_interaction,
                table,
                master_sim_context,
                saved_participants=[host, master_sim_chair, None, None]):
            log_host_action(
                'During Show To Table State',
                'Failed to Push Master Sim Interaction on {}'.format(
                    master_sim))
        else:
            log_host_action(
                'During Show To Table State',
                'Pushed Master Sim Interaction on {}'.format(master_sim))
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            log_host_action('During Show To Table State',
                            'Failed - unable to find zone director.')
            return
        for guest in group.all_sims_in_situation_gen():
            if guest is not master_sim:
                self.push_guest_take_seat_interaction(guest, zone_director)

    def push_guest_take_seat_interaction(self, guest, zone_director):
        (seat_id, _) = zone_director.get_sims_seat(guest)
        if seat_id is None:
            log_host_action(
                'During Show To Table Seat',
                "Failed - couldn't find a seat for the Sim {}.".format(guest))
            return
        seat = services.current_zone().object_manager.get(seat_id)
        if seat is None:
            log_host_action(
                'During Show To Table Seat',
                "Failed - couldn't find a seat for the Sim {}.".format(guest))
            return
        context = InteractionContext(guest, InteractionContext.SOURCE_SCRIPT,
                                     Priority.High)
        if not guest.push_super_affordance(self.guest_take_seat_interaction,
                                           seat, context):
            log_host_action('During Show To Table State',
                            'Failed to Push Guest Interaction')
        else:
            log_host_action('During Show To Table State',
                            'Push Guest Interaction on {}'.format(guest))

    def on_remove(self):
        super().on_remove()
        if self._alarm_handle is not None:
            self._alarm_handle.cancel()
            self._alarm_handle = None
예제 #15
0
class ScarecrowSituation(ObjectBoundSituationMixin, SituationComplexCommon):
    INSTANCE_TUNABLES = {
        '_situation_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for the Sim.\n            \n            This job should define a spawn affordance that will trigger\n            a continuation targeting the object the Sim spawns at.\n            ',
            tuning_group=GroupNames.SITUATION),
        '_do_stuff_state':
        _DoStuffState.TunableFactory(
            description=
            '\n            The state for the Sim doing stuff.\n            \n            This is the initial state after the Sim spawns onto the lot.\n\n            Any on-activate affordances run in this role will target\n            the object the Sim spawned near.\n            ',
            display_name='1. Do Stuff',
            tuning_group=GroupNames.STATE),
        '_leave_state':
        _LeaveState.TunableFactory(
            description=
            '\n            The state for the Sim leaving.\n            \n            Any on-activate affordances run in this role will target\n            the object the Sim spawned near.\n            ',
            display_name='2. Leave',
            tuning_group=GroupNames.STATE),
        '_spawn_object_targeting_affordance':
        TunableInteractionOfInterest(
            description=
            "\n            Affordance that runs targeting the object that the object that the\n            Sim had spawned at. This allows the situation to 'remember' that\n            object and when that object is destroyed, the situation will\n            be destroyed as well. \n            ",
            tuning_group=GroupNames.SITUATION),
        '_spawn_object_reset_loots':
        LootActions.TunableReference(
            description=
            '\n            Loots used to reset the object from which the scarecrow spawned from,\n            to handle cases for when the scarecrow Sim is not on lot during load.\n            ',
            tuning_group=GroupNames.SITUATION)
    }
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES

    @classmethod
    def _states(cls):
        return (SituationStateData(
            1, _DoStuffState,
            partial(cls._do_stuff_state, situation_job=cls._situation_job)),
                SituationStateData(
                    2, _LeaveState,
                    partial(cls._leave_state,
                            situation_job=cls._situation_job)))

    @classmethod
    def _get_tuned_job_and_default_role_state_tuples(cls):
        return [(cls._situation_job, None)]

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

    @classmethod
    def get_sims_expected_to_be_in_situation(cls):
        return 1

    def _get_role_state_overrides(self, sim, job_type, role_state_type,
                                  role_affordance_target):
        return (role_state_type,
                services.object_manager().get(self.bound_object_id))

    def start_situation(self):
        super().start_situation()
        for custom_key in self._spawn_object_targeting_affordance.custom_keys_gen(
        ):
            self._register_test_event(TestEvent.InteractionStart, custom_key)
        self._change_state(
            self._do_stuff_state(situation_job=self._situation_job))

    def load_situation(self):
        scarecrow_guest_info = next(
            iter(self._guest_list.get_persisted_sim_guest_infos()))
        if scarecrow_guest_info is None:
            self._reset_scarecrow_object()
            return False
        scarecrow_sim_info = services.sim_info_manager().get(
            scarecrow_guest_info.sim_id)
        if scarecrow_sim_info is None or scarecrow_sim_info.zone_id != services.current_zone_id(
        ):
            self._reset_scarecrow_object()
            return False
        return super().load_situation()

    def _reset_scarecrow_object(self):
        scarecrow_object = services.object_manager().get(self._bound_object_id)
        resolver = SingleObjectResolver(scarecrow_object)
        self._spawn_object_reset_loots.apply_to_resolver(resolver)

    def handle_event(self, sim_info, event, resolver):
        if event == TestEvent.InteractionStart:
            if self.is_sim_info_in_situation(sim_info) and resolver(
                    self._spawn_object_targeting_affordance):
                target = resolver.get_participant(ParticipantType.Object)
                if target is None:
                    logger.error(
                        '{}: {} target is None, cannot find the object for this situation to bind to!',
                        self, resolver)
                    self._self_destruct()
                    return
                self.bind_object(target)
        else:
            super().handle_event(sim_info, event, resolver)

    def go_to_leave_state(self):
        self._change_state(
            self._leave_state(situation_job=self._situation_job))

    def _gsi_additional_data_gen(self):
        if isinstance(self._cur_state, _DoStuffState):
            yield ('Time till Leave State',
                   str(self._cur_state.get_time_remaining()))
예제 #16
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()
예제 #17
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()
예제 #19
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
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)
예제 #21
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 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
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)
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)
예제 #25
0
class SpectralStalkerSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {'enter_state': _SpectralStalkerEnterState.TunableFactory(description='\n            The first state the Spectral Stalker starts in. This should handle any\n            spawning behavior. When the Interaction Of Interest completes, the \n            Stalker will be pushed to chase the target Sim.\n            ', tuning_group=STALKER_GROUP), 'chase_state': _SpectralStalkerChaseState.TunableFactory(description='\n            The stalker chases the target Sim.\n            ', tuning_group=STALKER_GROUP), 'idle_state': _SpectralStalkerIdleState.TunableFactory(description="\n            The stalker Idles until it's time to chase again.\n            ", tuning_group=STALKER_GROUP), 'exit_state': _SpectralStalkerExitState.TunableFactory(description='\n            The Spectral Stalker exits the world.\n            ', tuning_group=STALKER_GROUP), 'target_job': SituationJob.TunableReference(description='\n            The Situation Job on the target of the Spectral Stalker.\n            '), 'time_to_chase': TunableInterval(description='\n            The upper and lower limit for the amount of time, in Sim Minutes, to \n            chase a Sim before exiting the situation. When the Stalker starts, a \n            random time between these values will be chosen.\n            ', tunable_type=TunableSimMinute, default_lower=30, default_upper=120, minimum=1, tuning_group=STALKER_GROUP)}
    REMOVE_INSTANCE_TUNABLES = Situation.NON_USER_FACING_REMOVE_INSTANCE_TUNABLES
    TEST_EVENTS = (TestEvent.OnSimReset, TestEvent.SimDeathTypeSet)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._target_sim_info = None
        self._stalker_sim_info = None
        self._stalk_finish_ticks = None

    @classmethod
    def _states(cls):
        return [SituationStateData(0, _SpectralStalkerEnterState, cls.enter_state), SituationStateData(1, _SpectralStalkerChaseState, cls.chase_state), SituationStateData(2, _SpectralStalkerIdleState, cls.idle_state), SituationStateData(3, _SpectralStalkerExitState, cls.exit_state)]

    @classmethod
    def default_job(cls):
        pass

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

    def handle_event(self, sim_info, event, resolver):
        super().handle_event(sim_info, event, resolver)
        if sim_info is self._target_sim_info and (event == TestEvent.OnSimReset or event == TestEvent.SimDeathTypeSet):
            self._stalk_finish_ticks = None
            self._change_state(self.exit_state())

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

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        if job_type is self.target_job:
            self._target_sim_info = sim.sim_info
            services.get_event_manager().register(self, self.TEST_EVENTS)
        else:
            self._stalker_sim_info = sim.sim_info
            self._stalk_finish_ticks = services.time_service().sim_now.absolute_ticks() + create_time_span(minutes=self.time_to_chase.random_int()).in_ticks()

    def _self_destruct(self):
        services.get_event_manager().unregister(self, self.TEST_EVENTS)
        super()._self_destruct()

    def should_exit_situation(self):
        if not self._stalk_finish_ticks:
            return True
        return services.time_service().sim_now.absolute_ticks() >= self._stalk_finish_ticks
예제 #26
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 GroupInteractionSituation(SituationComplexCommon):
    INSTANCE_TUNABLES = {
        'pre_situation_state':
        TunableTuple(
            description=
            '\n            Information related to the pre interaction situation state.\n            ',
            situation_state=_PreSituationState.TunableFactory(
                description=
                '\n                The pre-interaction situation state. Get everyone to their positions\n                and idle.\n                '
            ),
            time_out=TunableSimMinute(
                description=
                '\n                How long this will last.\n                ',
                default=15,
                minimum=1),
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'constraint_affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The interaction that puts the followers into the constraint.\n            '
        ),
        'constraint_leader_affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The interaction that puts the leader into the constraint.\n            '
        ),
        'leader_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for leader.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'member_job':
        SituationJob.TunableReference(
            description=
            '\n            The situation job for member.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'interaction_state':
        _InteractionState.TunableFactory(
            description=
            '\n            The state that sim is doing the interaction.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP),
        'affordance':
        SuperInteraction.TunableReference(
            description=
            '\n            The affordance for leader sim to run when all sims have gathered.\n            ',
            tuning_group=GROUP_INTERACTION_TUNING_GROUP)
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._target_object = None
        self._routing_sims = []

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

    def start_situation(self):
        super().start_situation()
        self._create_situation_geometry()
        self._change_state(self.pre_situation_state.situation_state())

    @classmethod
    def _states(cls):
        return (SituationStateData(
            1,
            _PreSituationState,
            factory=cls.pre_situation_state.situation_state),
                SituationStateData(2,
                                   _InteractionState,
                                   factory=cls.interaction_state))

    @classmethod
    def default_job(cls):
        pass

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

    @property
    def should_route_sims_on_add(self):
        return False

    def _on_set_sim_job(self, sim, job_type):
        super()._on_set_sim_job(sim, job_type)
        self._route_sim(sim)

    def _get_ignored_object_ids(self):
        ignored_sim_ids = [sim.id for sim in self.all_sims_in_situation_gen()]
        return ignored_sim_ids

    def get_next_interaction_state(self):
        return self.interaction_state

    def _create_situation_geometry(self):
        seed = self._seed
        default_target_id = seed.extra_kwargs.get('default_target_id', None)
        if default_target_id is not None:
            self._target_object = services.object_manager().get(
                default_target_id)
        if self._target_object is None:
            default_location = seed.extra_kwargs.get('default_location', None)
            if default_location is not None:
                self._target_object = TerrainPoint(default_location)
            else:
                logger.error('Failed to determine target for {}', self)
                self._self_destruct()
                return
        else:
            leader_sim = self.initiating_sim_info.get_sim_instance()
            if leader_sim is None:
                logger.error('No leader sim for {}', self)
                self._self_destruct()
                return

    def _route_sim(self, sim):
        interaction_context = InteractionContext(
            sim, InteractionSource.SCRIPT_WITH_USER_INTENT, Priority.High)
        leader_sim = self.initiating_sim_info.get_sim_instance()
        if leader_sim is sim:
            affordance = self.constraint_leader_affordance
        else:
            affordance = self.constraint_affordance
        aop = AffordanceObjectPair(affordance, self._target_object, affordance,
                                   None)
        aop.test_and_execute(interaction_context)
        self._routing_sims.append(sim.id)

    def on_sim_finish_routing(self, sim_info):
        if sim_info.id in self._routing_sims:
            self._routing_sims.remove(sim_info.id)
            if not self._routing_sims:
                next_state = self.get_next_interaction_state()
                self._change_state(next_state())
        else:
            logger.error(
                'Sim {} finishes routing but not in routing sim list of situation {}',
                sim_info, self)

    def _cancel_constraint_affordance_for_sim(self, sim):
        for si in sim.get_all_running_and_queued_interactions():
            if si.affordance is self.constraint_affordance:
                si.cancel(
                    FinishingType.SITUATIONS,
                    cancel_reason_msg='Group Interaction Situation done.')
            if si.affordance is self.constraint_leader_affordance:
                si.cancel(
                    FinishingType.SITUATIONS,
                    cancel_reason_msg='Group Interaction Situation done.')

    def _on_remove_sim_from_situation(self, sim):
        self._cancel_constraint_affordance_for_sim(sim)
        super()._on_remove_sim_from_situation(sim)

    def _destroy(self):
        for sim in self.all_sims_in_situation_gen():
            self._cancel_constraint_affordance_for_sim(sim)
        self._target_object = None
        super()._destroy()
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)
예제 #29
0
class MultiSimFestivalSituation(BaseGenericFestivalSituation):
    INSTANCE_TUNABLES = {
        'group_filter':
        TunableReference(
            description=
            '\n            The aggregate filter that we use to find the sims for this\n            situation.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.SIM_FILTER),
            class_restrictions=filters.tunable.TunableAggregateFilter),
        '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',
            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))),
        'initial_state':
        MultiSimStartingFestivalSituationState.TunableFactory(
            description=
            '\n            The first state that the Sims will be put into when this Situation\n            Starts.\n            ',
            locked_args={
                'allow_join_situation': True,
                'time_out': None
            },
            tuning_group=SituationComplexCommon.SITUATION_STATE_GROUP),
        'blacklist_job':
        SituationJob.TunableReference(
            description=
            '\n            The default job used for blacklisting Sims from coming back as\n            festival goers.\n            '
        )
    }

    @classmethod
    def _states(cls):
        return (SituationStateData(1,
                                   MultiSimStartingFestivalSituationState,
                                   factory=cls.initial_state),
                SituationStateData(2,
                                   CooldownFestivalSituationState,
                                   factory=cls.cooldown_state))

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

    @classmethod
    def get_predefined_guest_list(cls):
        guest_list = SituationGuestList(invite_only=True)
        situation_manager = services.get_zone_situation_manager()
        worker_filter = cls.group_filter if cls.group_filter is not None else cls.default_job(
        ).filter
        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 = 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=worker_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 = cls.situation_job_mapping.get(result.tag, cls.default_job())
            guest_list.add_guest_info(
                SituationGuestInfo(result.sim_info.sim_id, job,
                                   RequestSpawningOption.DONT_CARE,
                                   BouncerRequestPriority.BACKGROUND_HIGH))
        return guest_list

    def start_situation(self):
        super().start_situation()
        self._change_state(self.initial_state())
예제 #30
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)