def _close_business(self, play_sound=True): if not self._is_open: return if play_sound: sound = PlaySound(services.get_active_sim(), self.tuning_data.audio_sting_close.instance) sound.start() self._employee_manager.close_business() self.send_daily_profit_and_cost_update() self._send_business_closed_telemetry() if self._owner_household_id is not None: owner_household = services.household_manager().get(self._owner_household_id) owner_household.bucks_tracker.deactivate_all_temporary_perk_timers_of_type(self.tuning_data.bucks) self.modify_funds(-self._employee_manager.final_daily_wages(), from_item_sold=False) self.on_store_closed() services.get_event_manager().process_event(TestEvent.BusinessClosed) self._distribute_business_open_status(False) if self.business_zone_id == services.current_zone_id(): self.tuning_data.lighting_helper_close.execute_lighting_helper(self) zone_director = services.venue_service().get_zone_director() if zone_director is not None: zone_director.refresh_open_street_director_status() else: self.run_off_lot_simulation() self._last_off_lot_update = None self._is_open = False self.show_summary_dialog(is_from_close=True) self._open_time = None
def play_sound(self, target_object): if self._sound is None: return sound = PlaySound(target_object, self._sound.instance) sound.start() self.apply_audio_effect(target_object) return sound
def play_looping_music_track(self, target_object): if self._music_track_snippet is None: return sound = PlaySound(target_object, self._music_track_snippet.looping_audio.instance) sound.start() self.apply_audio_effect(target_object) return sound
def _create_sound_alarm(self, *args, **kwargs): track_length = self._get_track_length() if self._sound_alarm is None: self._sound_alarm = alarms.add_alarm(self, track_length, self._sound_alarm_callback) if self._sound is None: self._sound = PlaySound(self._instrument, self._track.music_clip.instance) self._sound.start()
def _open_business(self): self._show_appropriate_open_business_notification() self._clear_state() self._is_open = True self._open_time = services.time_service().sim_now self._employee_manager.open_business() if self._owner_household_id is not None: owner_household = services.household_manager().get(self.owner_household_id) owner_household.bucks_tracker.activate_stored_temporary_perk_timers_of_type(self.tuning_data.bucks) self._distribute_business_open_status(is_open=True, open_time=self._open_time.absolute_ticks()) if self.business_zone_id == services.current_zone_id(): self.tuning_data.lighting_helper_open.execute_lighting_helper(self) zone_director = services.venue_service().get_zone_director() zone_director.set_customers_allowed(True) zone_director.refresh_open_street_director_status() else: self.start_off_lot_simulation_time() sound = PlaySound(services.get_active_sim(), self.tuning_data.audio_sting_open.instance) sound.start() self.send_daily_profit_and_cost_update() self._send_daily_items_sold_update() self._send_review_update_message()
def _create_sound_alarm(self, event_data, *args, **kwargs): if event_data is not None and event_data.event_data[ 'event_actor_id'] != self.sim.id: return if self._track is None: logger.error('Could not find a music track to play for {}', self, owner='rmccord') return track_length = self._get_track_length() if self._sound_alarm is None: self._sound_alarm = alarms.add_alarm(self, track_length, self._sound_alarm_callback) if self._sound is None and self._track.music_clip is not None: instrument = self._get_instrument() if instrument is not None: self._sound = PlaySound(instrument, self._track.music_clip.instance) self._sound.start() else: logger.error('Instrument is None for participant {} in {}', self.instrument_participant, self, owner='rmccord') mouthpiece_target = None if self.mouthpiece_target is None else self.get_participant( self.mouthpiece_target) for (musician, vocal_track) in self._musicians_and_vocals_gen(): interaction = None if musician.is_sim and musician is not self.sim and mouthpiece_target is not None: interaction = self._get_mouthpiece_interaction( mouthpiece_target, musician) if interaction is None: continue interaction.set_mouthpiece_owner(self) liability = self.get_liability( CANCEL_INTERACTION_ON_EXIT_LIABILITY) if liability is None: liability = CancelInteractionsOnExitLiability() self.add_liability(CANCEL_INTERACTION_ON_EXIT_LIABILITY, liability) liability.add_cancel_entry(musician, interaction) vocal = PlaySound(musician, vocal_track.vocal_clip.instance, is_vox=True) vocal.start() self._vocals[musician] = (vocal, interaction)
class PlayAudioMixin: INSTANCE_SUBCLASSES_ONLY = True INSTANCE_TUNABLES = { 'play_multiple_clips': Tunable( description= '\n If true, the Sim will continue playing until the interaction is\n cancelled or exit conditions are met. \n ', needs_tuning=False, tunable_type=bool, default=False), 'music_styles': TunableList( description= '\n List of music styles that are available for this interaction.\n ', tunable=MusicStyle.TunableReference( description= '\n A music style available for this interaction.\n ', pack_safe=True)), 'use_buffer': Tunable( description= "\n If true, this interaction will add the buffer tuned on the music\n track to the length of the track. This is tunable because some\n interactions, like Practice, use shorter audio clips that don't\n require the buffer.\n ", needs_tuning=False, tunable_type=bool, default=True), 'instrument_participant': TunableEnumEntry( description= '\n The participant that the music will play on.\n ', tunable_type=ParticipantTypeSingle, default=ParticipantTypeSingle.Object), 'audio_start_event': Tunable( description= '\n The script event to listen for from animation so we know when to\n start the music and vocals.\n ', tunable_type=int, default=100), 'audio_stop_event': Tunable( description= '\n The script event to listen for from animation so we know when to\n stop the music and vocals.\n ', tunable_type=int, default=101), 'mouthpiece_target': OptionalTunable( description= '\n The participant of mine that mouthpieces must target as their mouthpiece\n target. e.g. if they are targeting the actor sim of this interaction, \n their mouthpiece target would be targetsim, my mouthpiece target \n would be actor. If all of us are targeting a certain object then\n both would be object.\n ', tunable=TunableEnumEntry( description= '\n The participant of mine that mouthpieces must target.\n ', tunable_type=ParticipantTypeSingle, default=ParticipantTypeSingle.Object)) } def __init__(self, aop, context, track=None, pie_menu_category=None, unlockable_name=None, **kwargs): super().__init__(aop, context, **kwargs) self._track = track self.pie_menu_category = pie_menu_category self._unlockable_name = unlockable_name self._sound_alarm = None self._sound = None self._vocals = {} def build_basic_content(self, sequence=(), **kwargs): self.store_event_handler(self._create_sound_alarm, self.audio_start_event) self.store_event_handler(self._cancel_sound_alarm, self.audio_stop_event) sequence = super().build_basic_content(sequence, **kwargs) return build_critical_section_with_finally( sequence, self._cancel_sound_alarm_no_data) def _get_mouthpiece_interaction(self, mouthpiece_object, sim): for interaction in sim.get_all_running_and_queued_interactions(): if isinstance(interaction, PlayAudioMouthpieceSuperInteraction): if not interaction.is_finishing: if interaction.is_mouthpiece_target(mouthpiece_object): return interaction def _get_required_sims(self, *args, **kwargs): required_sims = super()._get_required_sims(*args, **kwargs) mouthpiece_target = None if self.mouthpiece_target is None else self.get_participant( self.mouthpiece_target) if mouthpiece_target is not None: for (musician, _) in self._musicians_and_vocals_gen(): if self._get_mouthpiece_interaction(mouthpiece_target, musician) is not None: required_sims.add(musician) return required_sims def _create_sound_alarm(self, event_data, *args, **kwargs): if event_data is not None and event_data.event_data[ 'event_actor_id'] != self.sim.id: return if self._track is None: logger.error('Could not find a music track to play for {}', self, owner='rmccord') return track_length = self._get_track_length() if self._sound_alarm is None: self._sound_alarm = alarms.add_alarm(self, track_length, self._sound_alarm_callback) if self._sound is None and self._track.music_clip is not None: instrument = self._get_instrument() if instrument is not None: self._sound = PlaySound(instrument, self._track.music_clip.instance) self._sound.start() else: logger.error('Instrument is None for participant {} in {}', self.instrument_participant, self, owner='rmccord') mouthpiece_target = None if self.mouthpiece_target is None else self.get_participant( self.mouthpiece_target) for (musician, vocal_track) in self._musicians_and_vocals_gen(): interaction = None if musician.is_sim and musician is not self.sim and mouthpiece_target is not None: interaction = self._get_mouthpiece_interaction( mouthpiece_target, musician) if interaction is None: continue interaction.set_mouthpiece_owner(self) liability = self.get_liability( CANCEL_INTERACTION_ON_EXIT_LIABILITY) if liability is None: liability = CancelInteractionsOnExitLiability() self.add_liability(CANCEL_INTERACTION_ON_EXIT_LIABILITY, liability) liability.add_cancel_entry(musician, interaction) vocal = PlaySound(musician, vocal_track.vocal_clip.instance, is_vox=True) vocal.start() self._vocals[musician] = (vocal, interaction) def _sound_alarm_callback(self, handle): if self.play_multiple_clips: self._cancel_sound_alarm(None) styles = self._get_music_styles() self._track = PlayAudioMixin._get_next_track( styles, self.sim, self.get_resolver()) self._create_sound_alarm(None) else: self.cancel( FinishingType.NATURAL, cancel_reason_msg= 'Sound alarm triggered and the song finished naturally.') def stop_mouthpiece(self, sim): if sim in self._vocals: (vocal, interaction) = self._vocals.pop(sim) vocal.stop() if interaction is not None: self.get_liability( CANCEL_INTERACTION_ON_EXIT_LIABILITY).remove_cancel_entry( sim, interaction) def _cancel_sound_alarm_no_data(self, *args, **kwargs): self._cancel_sound_alarm(None) def _cancel_sound_alarm(self, event_data, *args, **kwargs): if event_data is not None and event_data.event_data[ 'event_actor_id'] != self.sim.id: return if self._sound_alarm is not None: alarms.cancel_alarm(self._sound_alarm) self._sound_alarm = None if self._sound is not None: self._sound.stop() self._sound = None liability = self.get_liability(CANCEL_INTERACTION_ON_EXIT_LIABILITY) for (vocal, interaction) in self._vocals.values(): vocal.stop() if interaction is not None: interaction.set_mouthpiece_owner(None) liability.remove_cancel_entry(interaction.sim, interaction) self._vocals.clear() def _get_track_length(self): real_seconds = self._track.length if self.use_buffer: real_seconds += self._track.buffer interval = clock.interval_in_real_seconds(real_seconds) return interval def _get_instrument(self): return self.get_participant(self.instrument_participant) def _musicians_and_vocals_gen(self): resolver = self.get_resolver() for (participant_type, vocal_tracks) in self._track.vocals.items(): for participant in resolver.get_participants(participant_type): participant = participant.get_sim_instance( allow_hidden_flags=ALL_HIDDEN_REASONS_EXCEPT_UNINITIALIZED) if not isinstance( participant, sims.sim_info.SimInfo) or participant is None: logger.warn('Musician participant {} is None for {}', participant_type, self, owner='rmccord') else: for track in vocal_tracks: if track.tests.run_tests(resolver): yield (participant, track) break def _get_music_styles(self): return self.music_styles @staticmethod def _get_skill_level(skill_type, sim): skill = sim.get_statistic(skill_type, add=False) if skill is not None: return skill.get_user_value() elif skill_type.can_add(sim): return skill_type.get_user_value() return 0 @staticmethod def _get_next_track(styles, sim, resolver): valid_tracks = [] styles = set(styles) for (skill_type, level_to_tracks) in MusicStyle.tracks_by_skill.items(): skill_level = PlayAudioMixin._get_skill_level(skill_type, sim) for track in level_to_tracks[skill_level]: if not styles & MusicStyle.styles_for_track[track]: continue valid_tracks.append(track) sim_mood = sim.get_mood() valid_mood_tracks = [ track for track in valid_tracks if sim_mood in track.moods ] if not valid_mood_tracks and not valid_tracks: return to_consider = valid_mood_tracks or valid_tracks random.shuffle(to_consider) for track in to_consider: if track.check_for_unlock and sim.sim_info.unlock_tracker is not None and sim.sim_info.unlock_tracker.is_unlocked( track): return track if not track.check_for_unlock: if track.tests.run_tests(resolver): return track @classmethod def _has_tracks(cls, sim, resolver): has_tracker = sim.sim_info.unlock_tracker is not None styles = set(cls.music_styles) for (skill_type, level_to_tracks) in MusicStyle.tracks_by_skill.items(): skill_level = PlayAudioMixin._get_skill_level(skill_type, sim) for track in level_to_tracks[skill_level]: if not styles & MusicStyle.styles_for_track[track]: continue if track.check_for_unlock and has_tracker and sim.sim_info.unlock_tracker.is_unlocked( track): return True if not track.check_for_unlock and track.tests.run_tests( resolver): return True return False @classmethod def _verify_tuning_callback(cls): if cls.is_super: for affordance in cls._content_sets.all_affordances_gen(): if isinstance(affordance, PlayAudioMixin): logger.error( '{} references another PlayAudio interaction: {} in its content set. This will not properly work as the clip events will collide with one another.', cls, affordance)
class PlayAudioSuperInteraction(SuperInteraction): __qualname__ = 'PlayAudioSuperInteraction' INSTANCE_SUBCLASSES_ONLY = True SCRIPT_EVENT_ID_START_AUDIO = 100 SCRIPT_EVENT_ID_STOP_AUDIO = 101 INSTANCE_TUNABLES = { 'play_multiple_clips': Tunable( description= '\n If true, the Sim will continue playing until the interaction is\n cancelled or exit conditions are met. \n ', needs_tuning=False, tunable_type=bool, default=False), 'music_styles': TunableList( TunableReference( description= '\n Which music styles are available for this interaction.\n ', manager=services.get_instance_manager( sims4.resources.Types.RECIPE), class_restrictions=(MusicStyle, ))), 'use_buffer': Tunable( description= "\n If true, this interaction will add the buffer tuned on the music\n track to the length of the track. This is tunable because some\n interactions, like Practice, use shorter audio clips that don't\n require the buffer.\n ", needs_tuning=False, tunable_type=bool, default=True) } def __init__(self, aop, context, track=None, pie_menu_category=None, unlockable_name=None, **kwargs): super().__init__(aop, context, **kwargs) self._track = track self.pie_menu_category = pie_menu_category self._unlockable_name = unlockable_name self._sound_alarm = None self._sound = None def build_basic_content(self, sequence=(), **kwargs): self.animation_context.register_event_handler( self._create_sound_alarm, handler_id=self.SCRIPT_EVENT_ID_START_AUDIO) self.animation_context.register_event_handler( self._cancel_sound_alarm, handler_id=self.SCRIPT_EVENT_ID_STOP_AUDIO) return super().build_basic_content(sequence, **kwargs) def on_reset(self): self._cancel_sound_alarm() super().on_reset() def _create_sound_alarm(self, *args, **kwargs): track_length = self._get_track_length() if self._sound_alarm is None: self._sound_alarm = alarms.add_alarm(self, track_length, self._sound_alarm_callback) if self._sound is None: self._sound = PlaySound(self._instrument, self._track.music_clip.instance) self._sound.start() def _sound_alarm_callback(self, handle): if self.play_multiple_clips: self._cancel_sound_alarm() if hasattr(self, 'recipe'): styles = [self.recipe.music_style] else: styles = self.music_styles self._track = PlayAudioSuperInteraction._get_next_track( styles, self.sim, self.get_resolver()) self._create_sound_alarm() else: self.cancel( FinishingType.NATURAL, cancel_reason_msg= 'Sound alarm triggered and the song finished naturally.') def _cancel_sound_alarm(self, *args, **kwargs): if self._sound_alarm is not None: alarms.cancel_alarm(self._sound_alarm) self._sound_alarm = None if self._sound is not None: self._sound.stop() self._sound = None def _get_track_length(self): real_seconds = self._track.length if self.use_buffer: real_seconds += self._track.buffer interval = clock.interval_in_real_seconds(real_seconds) return interval @property def _instrument(self): return self.target @staticmethod def _get_next_track(styles, sim, resolver): valid_tracks = [] for style in styles: for track in style.music_tracks: if track.check_for_unlock and sim.sim_info.unlock_tracker.is_unlocked( track): valid_tracks.append(track) else: while not track.check_for_unlock and track.tests.run_tests( resolver): valid_tracks.append(track) sim_mood = sim.get_mood() valid_mood_tracks = tuple(track for track in valid_tracks if sim_mood in track.moods) return random.choice(valid_mood_tracks or valid_tracks)
class PlayAudioSuperInteraction(SuperInteraction): __qualname__ = 'PlayAudioSuperInteraction' INSTANCE_SUBCLASSES_ONLY = True SCRIPT_EVENT_ID_START_AUDIO = 100 SCRIPT_EVENT_ID_STOP_AUDIO = 101 INSTANCE_TUNABLES = {'play_multiple_clips': Tunable(description='\n If true, the Sim will continue playing until the interaction is\n cancelled or exit conditions are met. \n ', needs_tuning=False, tunable_type=bool, default=False), 'music_styles': TunableList(TunableReference(description='\n Which music styles are available for this interaction.\n ', manager=services.get_instance_manager(sims4.resources.Types.RECIPE), class_restrictions=(MusicStyle,))), 'use_buffer': Tunable(description="\n If true, this interaction will add the buffer tuned on the music\n track to the length of the track. This is tunable because some\n interactions, like Practice, use shorter audio clips that don't\n require the buffer.\n ", needs_tuning=False, tunable_type=bool, default=True)} def __init__(self, aop, context, track=None, pie_menu_category=None, unlockable_name=None, **kwargs): super().__init__(aop, context, **kwargs) self._track = track self.pie_menu_category = pie_menu_category self._unlockable_name = unlockable_name self._sound_alarm = None self._sound = None def build_basic_content(self, sequence=(), **kwargs): self.animation_context.register_event_handler(self._create_sound_alarm, handler_id=self.SCRIPT_EVENT_ID_START_AUDIO) self.animation_context.register_event_handler(self._cancel_sound_alarm, handler_id=self.SCRIPT_EVENT_ID_STOP_AUDIO) return super().build_basic_content(sequence, **kwargs) def on_reset(self): self._cancel_sound_alarm() super().on_reset() def _create_sound_alarm(self, *args, **kwargs): track_length = self._get_track_length() if self._sound_alarm is None: self._sound_alarm = alarms.add_alarm(self, track_length, self._sound_alarm_callback) if self._sound is None: self._sound = PlaySound(self._instrument, self._track.music_clip.instance) self._sound.start() def _sound_alarm_callback(self, handle): if self.play_multiple_clips: self._cancel_sound_alarm() if hasattr(self, 'recipe'): styles = [self.recipe.music_style] else: styles = self.music_styles self._track = PlayAudioSuperInteraction._get_next_track(styles, self.sim, self.get_resolver()) self._create_sound_alarm() else: self.cancel(FinishingType.NATURAL, cancel_reason_msg='Sound alarm triggered and the song finished naturally.') def _cancel_sound_alarm(self, *args, **kwargs): if self._sound_alarm is not None: alarms.cancel_alarm(self._sound_alarm) self._sound_alarm = None if self._sound is not None: self._sound.stop() self._sound = None def _get_track_length(self): real_seconds = self._track.length if self.use_buffer: real_seconds += self._track.buffer interval = clock.interval_in_real_seconds(real_seconds) return interval @property def _instrument(self): return self.target @staticmethod def _get_next_track(styles, sim, resolver): valid_tracks = [] for style in styles: for track in style.music_tracks: if track.check_for_unlock and sim.sim_info.unlock_tracker.is_unlocked(track): valid_tracks.append(track) else: while not track.check_for_unlock and track.tests.run_tests(resolver): valid_tracks.append(track) sim_mood = sim.get_mood() valid_mood_tracks = tuple(track for track in valid_tracks if sim_mood in track.moods) return random.choice(valid_mood_tracks or valid_tracks)