def __call__(self, buff=None, data_object=None, objective_guid64=None): if buff is None: return TestResult( False, 'Buff provided is None, valid during zone load.') if buff not in self.buffs_to_check: return TestResult( False, 'Buff provided is not among the buffs you are looking for.') buff_uptime = TimeSpan(0) if self.buff_test_type == self.BuffTestType.SUM_OF_BUFFS: for buff_tuning in self.buffs_to_check: buff_uptime += data_object.get_total_buff_uptime(buff_tuning) else: buff_uptime = data_object.get_total_buff_uptime(buff) relative_start_value = data_object.get_starting_values( objective_guid64) if relative_start_value is not None: ticks = 0 buff_uptime -= TimeSpan(relative_start_value[ticks]) if buff_uptime >= self.length_of_time: return TestResult.TRUE run_time = self.length_of_time.in_hours() - (self.length_of_time - buff_uptime).in_hours() return TestResultNumeric( False, 'BuffForAmountOfTimeTest: Buff has not existed long enough.', current_value=run_time, goal_value=self.length_of_time.in_hours(), is_money=False)
def __init__(self, *args, reader=None, **kwargs): super().__init__(reader=reader, *args, **kwargs) self._total_time_ran = TimeSpan.ZERO self._last_started_time = None self._alarm_handle = None self._total_duration = interval_in_sim_hours(self._goal_test.duration) self._test_events = set() self._test_events.add( event_testing.test_events.TestEvent.InteractionStart) self._test_events.add( event_testing.test_events.TestEvent.InteractionComplete) services.get_event_manager().register(self, self._test_events) if reader is not None: duration_run = reader.read_uint64(self.DURATION_RUN, 0) self._total_time_ran = TimeSpan(duration_run) self._sims_running_interaction = set() if self._situation is None and self._sim_info is not None: self._sims_running_interaction.add(self._actor_ref().id) else: for sim in self._situation.all_sims_in_situation_gen(): while sim.si_state.is_running_affordance( self._goal_test.affordance): self._sims_running_interaction.add(sim.id) if self._sims_running_interaction: self._start_alarm()
def setup(self, gameplay_zone_data=None, save_slot_data=None): if gameplay_zone_data is None: return world_game_time = self._initial_ticks if save_slot_data is not None and save_slot_data.HasField( 'gameplay_data'): world_game_time = save_slot_data.gameplay_data.world_game_time self._zone_init_world_game_time = DateAndTime(world_game_time) initial_time = world_game_time if gameplay_zone_data.HasField('game_time'): saved_ticks = gameplay_zone_data.game_time tick_diff = world_game_time - saved_ticks time_diff = TimeSpan(tick_diff) self._time_of_last_save = DateAndTime(saved_ticks) if time_diff.in_minutes( ) < PersistenceTuning.MAX_LOT_SIMULATE_ELAPSED_TIME: initial_time = saved_ticks else: max_minutes = date_and_time.create_date_and_time( minutes=PersistenceTuning.MAX_LOT_SIMULATE_ELAPSED_TIME) initial_time = world_game_time - max_minutes.absolute_ticks() self._initial_ticks = initial_time if gameplay_zone_data.HasField('clock_speed_mode'): self._client_connect_speed = ClockSpeedMode( gameplay_zone_data.clock_speed_mode) else: self._client_connect_speed = ClockSpeedMode.NORMAL
def load(self, **__): save_slot_data_msg = services.get_persistence_service( ).get_save_slot_proto_buff() if save_slot_data_msg.gameplay_data.HasField('organization_service'): data = save_slot_data_msg.gameplay_data.organization_service for org_data in data.organizations: members_list = [] for org_member_data in org_data.organization_members: members_list.append(org_member_data.organization_member_id) self._organization_members[ org_data.organization_id] = members_list for schedule_cancelled_event_data in data.schedule_cancelled_event_data: if schedule_cancelled_event_data.HasField( 'schedule_venue_event_time'): if schedule_cancelled_event_data.HasField('org_event_id'): alarm_handle = alarms.add_alarm( self, TimeSpan(schedule_cancelled_event_data. schedule_venue_event_time), lambda *_: self. _schedule_cancelled_organization_event( schedule_cancelled_event_data.org_event_id), cross_zone=True) self._schedule_cancelled_venue_event_alarm[ schedule_cancelled_event_data. org_event_id] = alarm_handle
def get_time_until_next_wakeup(self, offset_time: TimeSpan = None): now = services.time_service().sim_now time_span_until_wakeup = None sim_careers = self.owner.sim_info.careers if sim_careers: earliest_time = None for career in sim_careers.values(): wakeup_time = career.get_next_wakeup_time() if not earliest_time is None: if wakeup_time < earliest_time: earliest_time = wakeup_time earliest_time = wakeup_time if earliest_time is not None: if offset_time is not None: time_to_operate = now + offset_time else: time_to_operate = now time_span_until_wakeup = time_to_operate.time_till_next_day_time( earliest_time) if time_span_until_wakeup is None: start_time = self._get_default_sleep_schedule_work_time( offset_time) time_span_until_wakeup = start_time - now if time_span_until_wakeup.in_ticks() <= 0: time_span_until_wakeup += TimeSpan( date_and_time.sim_ticks_per_day()) logger.assert_log(time_span_until_wakeup.in_ticks() > 0, 'time_span_until_wakeup occurs in the past.') return time_span_until_wakeup
def enable_sim_motive_graph_logging(*args, enableLog=False, **kwargs): global sim_motive_graph_alarm if enableLog and sim_motive_graph_alarm is None: sim_motive_graph_alarm = alarms.add_alarm(sim_motive_archiver, TimeSpan(5000), lambda _: archive_sim_motives(), repeating=True) else: alarms.cancel_alarm(sim_motive_graph_alarm) sim_motive_graph_alarm = None
def _create_curfew_callback(self, now, time): if time is not self.UNSET: alarm_time = date_and_time.create_date_and_time(hours=time) curfew_span = now.time_till_next_day_time(alarm_time) if curfew_span.in_ticks() == 0: curfew_span += TimeSpan(date_and_time.sim_ticks_per_day()) self._curfew_started_alarm_handle = alarms.add_alarm( self, curfew_span, self._handle_curfew_callback, False)
def _create_warning_callback(self, now, time): if time is not CurfewService.UNSET: alarm_time = date_and_time.create_date_and_time(hours=time - 1) warning_span = now.time_till_next_day_time(alarm_time) if warning_span.in_ticks() == 0: warning_span += TimeSpan(date_and_time.sim_ticks_per_day()) self._curfew_warning_alarm_handle = alarms.add_alarm( self, warning_span, self._handle_warning_callback, False)
def set_update_alarm(self): self.sim_time_on_connect = services.time_service().sim_now self.server_time_on_connect = services.server_clock_service().now() self.sim_time_last_update = self.sim_time_on_connect self.server_time_last_update = self.server_time_on_connect self.update_alarm_handle = alarms.add_alarm( self, TimeSpan(self.TIME_DATA_UPDATE_RATE), self._update_timer_alarm, True)
def load_data(self, household_proto): self._missing_pet_id = household_proto.missing_pet_tracker_data.missing_pet_id self._return_pet_on_zone_load = household_proto.missing_pet_tracker_data.return_pet_on_zone_load self._running_away = household_proto.missing_pet_tracker_data.running_away self._alert_posted = household_proto.missing_pet_tracker_data.alert_posted if household_proto.missing_pet_tracker_data.test_alarm_finishing_time != 0: test_alarm_interval = household_proto.missing_pet_tracker_data.test_alarm_finishing_time test_interval = TimeSpan(test_alarm_interval) self._add_test_alarm(test_interval) if household_proto.missing_pet_tracker_data.return_alarm_finishing_time != 0: return_alarm_interval = household_proto.missing_pet_tracker_data.return_alarm_finishing_time return_interval = TimeSpan(return_alarm_interval) self._add_return_alarm(return_interval) if household_proto.missing_pet_tracker_data.cooldown_alarm_finishing_time != 0: cooldown_alarm_interval = household_proto.missing_pet_tracker_data.cooldown_alarm_finishing_time cooldown_interval = clock.TimeSpan(cooldown_alarm_interval) self._add_cooldown_alarm(cooldown_interval)
def __init__(self, *args, reader=None, **kwargs): super().__init__(*args, reader=reader, **kwargs) self._total_time_ran = TimeSpan.ZERO self._last_started_time = None self._alarm_handle = None self._total_duration = interval_in_sim_hours(self._goal_test.duration) for custom_key in self._goal_test.custom_keys_gen(): services.get_event_manager().register_with_custom_key( self, event_testing.test_events.TestEvent.InteractionStart, custom_key) services.get_event_manager().register_with_custom_key( self, event_testing.test_events.TestEvent.InteractionComplete, custom_key) if reader is not None: duration_run = reader.read_uint64(self.DURATION_RUN, 0) self._total_time_ran = TimeSpan(duration_run) self._sims_running_interaction = set()
def _create_curfew_ended_callback(self, now, time): if time is not CurfewService.UNSET: alarm_time = date_and_time.create_date_and_time( hours=CurfewService.CURFEW_END_TIME) curfew_span = now.time_till_next_day_time(alarm_time) if curfew_span.in_ticks() == 0: curfew_span += TimeSpan(date_and_time.sim_ticks_per_day()) self._curfew_ended_alarm_handle = alarms.add_alarm( self, curfew_span, self._handle_curfew_ended_callback, False)
def schedule_duration_alarm(self, callback, cross_zone=False): if self._duration_override is not None: time_span = TimeSpan(self._duration_override) else: time_span = create_time_span(minutes=self.duration) return alarms.add_alarm(self, time_span, callback, cross_zone=cross_zone)
def _add_alarm(self): now = services.time_service().sim_now time_span = now.time_till_next_day_time(Retirement.DAILY_PAY_TIME) if time_span == TimeSpan.ZERO: time_span = time_span + TimeSpan(date_and_time.sim_ticks_per_day()) self._alarm_handle = alarms.add_alarm(self._sim_info, time_span, self._alarm_callback, repeating=False, use_sleep_time=False)
def _create_full_autonomy_alarm(self, time_until_trigger): if self._full_autonomy_alarm_handle is not None: self._destroy_full_autonomy_alarm() if time_until_trigger.in_ticks() <= 0: time_until_trigger = TimeSpan(1) self._full_autonomy_alarm_handle = alarms.add_alarm( self, time_until_trigger, self._on_run_full_autonomy_callback, use_sleep_time=False)
def preroll(self, time_to_preroll): if time_to_preroll is None: return self._run_preroll() time_spent = self._get_fake_preroll_time() if time_spent is None: return TimeSpan(0) time_left = time_to_preroll - time_spent if time_left > TimeSpan.ZERO: self._preroll_end_of_state() return time_left
def _set_next_daily_random_voting_alarm(self): if self._voting_daily_random_alarm is not None: alarms.cancel_alarm(self._voting_daily_random_alarm) if not self.voting_open: self._voting_daily_random_alarm = None return time_of_day = create_date_and_time(hours=0) now = services.time_service().sim_now daily_random_voting_span = now.time_till_next_day_time(time_of_day) if daily_random_voting_span == TimeSpan.ZERO: daily_random_voting_span += TimeSpan(sim_ticks_per_day()) self._voting_daily_random_alarm = alarms.add_alarm(self, daily_random_voting_span, lambda _: self._do_daily_random_voting(), cross_zone=True)
def add_tag_time_update_from_interaction(self, interaction=None, **kwargs): if interaction is None: return type = data_const.DataType.TagData if interaction.id not in self._data[type].interactions: self._data[type].interactions[interaction.id] = {} for tag in interaction.get_category_tags(): previous = TimeSpan(0) if tag in self._data[type].interactions[interaction.id]: previous = self._data[type].interactions[interaction.id][tag] time_update = interaction.consecutive_running_time_span - previous self._data[type].time_added(tag, time_update) self._data[type].interactions[interaction.id][ tag] = interaction.consecutive_running_time_span
def load(self, save_data, skip_load=False): self._careers.clear() for career_save_data in save_data.careers: career_uid = career_save_data.career_uid career = services.get_instance_manager( sims4.resources.Types.CAREER).get(career_uid) while career is not None: career_inst = career(self._sim_info) career_inst._current_track = services.get_instance_manager( sims4.resources.Types.CAREER_TRACK).get( career_save_data.track_uid) career_inst._level = career_save_data.track_level career_inst._user_level = career_save_data.user_display_level career_inst._company_name = career_save_data.company_name_hash if skip_load: career_inst._join_time = services.time_service().sim_now else: career_inst._join_time = DateAndTime( career_save_data.join_time) career_inst._attended_work = career_save_data.attended_work career_inst._called_in_sick = career_save_data.called_in_sick career_inst._pending_promotion = career_save_data.pending_promotion career_inst._career_situation_id = career_save_data.active_situation_id if career_save_data.career_situation_guid != 0: career_inst._career_situation = services.get_instance_manager( sims4.resources.Types.CAREER_SITUATION).get( career_save_data.career_situation_guid) if career_save_data.HasField('current_work_start'): career_inst._current_work_start = DateAndTime( career_save_data.current_work_start) career_inst._current_work_end = DateAndTime( career_save_data.current_work_end) career_inst._current_work_duration = TimeSpan( career_save_data.current_work_duration) career_inst.career_start(is_load=True) self._careers[career_uid] = career_inst self._career_history.clear() for history_entry in save_data.career_history: if skip_load and history_entry.career_uid not in self._careers: pass self._career_history[history_entry.career_uid] = CareerHistory( history_entry.track_level, history_entry.user_display_level, DateAndTime(history_entry.time_left), history_entry.track_uid, history_entry.highest_level) self._retirement = None if save_data.HasField('retirement_career_uid'): retired_career = save_data.retirement_career_uid self._retirement = Retirement(self._sim_info, retired_career)
def _alarm_callback(self, handle, alarm_datas=None): if alarm_datas is None: alarm_datas = self._alarm_data.pop(self._alarm_handle) self._alarm_handle = None if self._start_callback is not None: for alarm_data in alarm_datas: if alarm_data.is_random: current_time = services.time_service().sim_now current_time = current_time.time_since_beginning_of_week() available_time_span = alarm_data.end_time - current_time random_time_span = TimeSpan(random.randint(0, available_time_span.in_ticks())) random_callback = functools.partial(self._random_alarm_callback, alarm_data=alarm_data) cur_handle = alarms.add_alarm(self, random_time_span, random_callback, cross_zone=self._cross_zone) self._random_alarm_handles.append(cur_handle) else: self._start_callback(self, alarm_data, self.extra_data) self.schedule_next_alarm(schedule_immediate=False)
def _set_up_bill_timer(self): day = self.TIME_TO_PLACE_BILL_IN_HIDDEN_INVENTORY.day hour = self.TIME_TO_PLACE_BILL_IN_HIDDEN_INVENTORY.hour minute = self.TIME_TO_PLACE_BILL_IN_HIDDEN_INVENTORY.minute time = create_date_and_time(days=day, hours=hour, minutes=minute) time_until_bill_delivery = services.time_service( ).sim_now.time_to_week_time(time) bill_delivery_time = services.time_service( ).sim_now + time_until_bill_delivery end_of_first_week = DateAndTime(0) + interval_in_sim_weeks(1) if bill_delivery_time < end_of_first_week: time_until_bill_delivery += interval_in_sim_weeks(1) if time_until_bill_delivery.in_ticks() <= 0: time_until_bill_delivery = TimeSpan(1) self._bill_timer_handle = alarms.add_alarm( self, time_until_bill_delivery, lambda _: self.allow_bill_delivery())
def get_time_until_next_update(self, mode=FullAutonomy): time_to_run_autonomy = None if self.is_player_active(): time_to_run_autonomy = self._get_last_user_directed_action_time( ) + mode.get_autonomy_delay_after_user_interaction() elif self._last_autonomy_result_was_none: time_to_run_autonomy = self._get_last_no_result_time( ) + mode.get_no_result_delay_time() elif self.owner.has_any_pending_or_running_interactions(): time_to_run_autonomy = self._get_last_autonomous_action_time( ) + mode.get_autonomous_delay_time() else: time_to_run_autonomy = self._get_last_autonomous_action_time( ) + mode.get_autonomous_update_delay_with_no_primary_sis() delta_time = time_to_run_autonomy - services.time_service().sim_now if delta_time.in_ticks() <= 0: delta_time = TimeSpan(1) return delta_time
def _alarm_callback(self, handle, alarm_datas=None): if alarm_datas is None: alarm_datas = self._alarm_data.pop(self._alarm_handle) if self._start_callback is not None: for alarm_data in alarm_datas: start_time = alarm_data.start_time end_time = alarm_data.end_time is_random = alarm_data.is_random if is_random: cur_time_span = end_time - start_time random_time_span = TimeSpan(random.randint( 0, cur_time_span.in_ticks())) random_callback = lambda handle: self._random_alarm_callback(handle, alarm_data) cur_handle = alarms.add_alarm(self, random_time_span, random_callback) self._random_alarm_handles.append(cur_handle) else: self._start_callback(self, alarm_data, self.extra_data) self._schedule_next_alarm(schedule_immediate=False)
def get_time_until_next_update(self, mode=FullAutonomy): time_to_run_autonomy = None if self.is_player_active(): time_to_run_autonomy = self._get_last_user_directed_action_time( ) + mode.get_autonomy_delay_after_user_interaction() elif self._last_autonomy_result_was_none: time_to_run_autonomy = self._get_last_no_result_time( ) + mode.get_no_result_delay_time() elif self.owner.si_state.has_visible_si( ignore_pending_complete=True ) or self.owner.transition_controller is not None and self.owner.transition_controller.interaction.visible and not self.owner.transition_controller.interaction.is_finishing or self.owner.queue.visible_len( ) > 0: time_to_run_autonomy = self._get_last_autonomous_action_time( ) + mode.get_autonomous_delay_time() else: time_to_run_autonomy = self._get_last_autonomous_action_time( ) + mode.get_autonomous_update_delay_with_no_primary_sis() delta_time = time_to_run_autonomy - services.time_service().sim_now if delta_time.in_ticks() <= 0: delta_time = TimeSpan(1) return delta_time
def gc_sample_log(rate: int = 30, real_time: bool = False, _connection=None): global gc_alarm_handle if gc_alarm_handle is None: gc_output('Enabled sample logging.') if real_time: ticks = rate * TICKS_PER_REAL_WORLD_SECOND gc_alarm_handle = alarms.add_alarm_real_time( services.current_zone(), TimeSpan(ticks), garbage_sample_handle, repeating=True, use_sleep_time=False) else: gc_alarm_handle = alarms.add_alarm(services.current_zone(), create_time_span(minutes=rate), garbage_sample_handle, repeating=True) else: gc_output('Disabling sample logging.') gc_alarm_handle.cancel() gc_alarm_handle = None
def __init__(self, *args, reader=None, **kwargs): super().__init__(reader=reader, *args, **kwargs) self._total_time_ran = TimeSpan.ZERO self._last_started_time = None self._alarm_handle = None self._total_duration = interval_in_sim_hours(self._goal_test.duration) self._test_events = set() self._test_events.add(event_testing.test_events.TestEvent.InteractionStart) self._test_events.add(event_testing.test_events.TestEvent.InteractionComplete) services.get_event_manager().register(self, self._test_events) if reader is not None: duration_run = reader.read_uint64(self.DURATION_RUN, 0) self._total_time_ran = TimeSpan(duration_run) self._sims_running_interaction = set() if self._situation is None and self._sim_info is not None: self._sims_running_interaction.add(self._actor_ref().id) else: for sim in self._situation.all_sims_in_situation_gen(): while sim.si_state.is_running_affordance(self._goal_test.affordance): self._sims_running_interaction.add(sim.id) if self._sims_running_interaction: self._start_alarm()
def _fall_to_ground(self): self._cancel_fall_to_ground_retry_alarm() if not self.is_on_tree: return if self.owner.in_use: self._fall_to_ground_retry_alarm_handle = alarms.add_alarm(self.owner, TimeSpan.in_real_world_seconds(10), self._fall_to_ground) return parent_obj = self.owner.parent if parent_obj is None: logger.warn('{}: Fruit failed to fall, it is no longer parented.', self.owner) return target_location = routing.Location(self.owner.routing_location.position, parent_obj.routing_location.orientation, parent_obj.routing_location.routing_surface) context = FindGoodLocationContext(starting_routing_location=target_location, object_footprints=(self.plant.get_footprint(0),), max_distance=self.fruit_fall_behavior.search_radius.upper_bound) (translation, orientation) = find_good_location(context) if translation is None or orientation is None: logger.warn('{}: Failed to fall because FGL failed.', self.owner) self.owner.destroy(source=parent_obj, cause='Failed to fall because FGL failed') return if self.owner.parent is not None: self.owner.clear_parent(sims4.math.Transform(translation, orientation), self.owner.routing_surface) else: self.owner.set_location(sims4.math.Location(sims4.math.Transform(translation, orientation), self.owner.routing_surface))
def _fall_to_ground(self): self._cancel_fall_to_ground_retry_alarm() if not self.is_on_tree: return if self.owner.in_use: self._fall_to_ground_retry_alarm_handle = alarms.add_alarm( self.owner, TimeSpan.in_real_world_seconds(10), self._fall_to_ground) return parent_obj = self.owner.parent if parent_obj is None: logger.warn('{}: Fruit failed to fall, it is no longer parented.', self.owner) return target_location = routing.Location( self.owner.routing_location.position, parent_obj.routing_location.orientation, parent_obj.routing_location.routing_surface) context = FindGoodLocationContext( starting_routing_location=target_location, object_footprints=(self.plant.get_footprint(0), ), max_distance=self.fruit_fall_behavior.search_radius.upper_bound) (translation, orientation) = find_good_location(context) if translation is None or orientation is None: logger.warn('{}: Failed to fall because FGL failed.', self.owner) self.owner.destroy(source=parent_obj, cause='Failed to fall because FGL failed') return if self.owner.parent is not None: self.owner.clear_parent( sims4.math.Transform(translation, orientation), self.owner.routing_surface) else: self.owner.set_location( sims4.math.Location( sims4.math.Transform(translation, orientation), self.owner.routing_surface))
def save_relative_start_values(self, objective_guid64, data_object): buff_uptime = TimeSpan(0) for buff_tuning in self.buffs_to_check: buff_uptime += data_object.get_total_buff_uptime(buff_tuning) data_object.set_starting_values(objective_guid64, [buff_uptime.in_ticks()])
def _start_voting_timer(self, voting_time_of_week, callback): (time, voting_time) = self._times_from_voting_time_of_week(services.time_service().sim_now, voting_time_of_week) if voting_time.in_ticks() <= 0: voting_time = TimeSpan(voting_time.in_ticks() + sim_ticks_per_week()) voting_alarm = alarms.add_alarm(self, voting_time, callback, cross_zone=True) return (time, voting_alarm)
class SituationGoalInteractionTime(SituationGoal): __qualname__ = 'SituationGoalInteractionTime' DURATION_RUN = 'duration_run' REMOVE_INSTANCE_TUNABLES = ('_post_tests',) INSTANCE_TUNABLES = {'_goal_test': TunableInteractionOfInterest(description='\n Interaction and duration that this situation goal will use.\n Example: Bartend for 10 sim minutes.\n ', tuning_group=GroupNames.TESTS)} def __init__(self, *args, reader=None, **kwargs): super().__init__(reader=reader, *args, **kwargs) self._total_time_ran = TimeSpan.ZERO self._last_started_time = None self._alarm_handle = None self._total_duration = interval_in_sim_hours(self._goal_test.duration) self._test_events = set() self._test_events.add(event_testing.test_events.TestEvent.InteractionStart) self._test_events.add(event_testing.test_events.TestEvent.InteractionComplete) services.get_event_manager().register(self, self._test_events) if reader is not None: duration_run = reader.read_uint64(self.DURATION_RUN, 0) self._total_time_ran = TimeSpan(duration_run) self._sims_running_interaction = set() if self._situation is None and self._sim_info is not None: self._sims_running_interaction.add(self._actor_ref().id) else: for sim in self._situation.all_sims_in_situation_gen(): while sim.si_state.is_running_affordance(self._goal_test.affordance): self._sims_running_interaction.add(sim.id) if self._sims_running_interaction: self._start_alarm() def create_seedling(self): if self._alarm_handle is not None: self._start_alarm() seedling = super().create_seedling() writer = seedling.writer writer.write_uint64(self.DURATION_RUN, self._total_time_ran.in_ticks()) return seedling def decommision(self): services.get_event_manager().unregister(self, self._test_events) self._stop_alarm() super().decommision() def _on_hour_reached(self, alarm_handle=None): self._stop_alarm() if self._total_time_ran >= self._total_duration: super()._on_goal_completed() else: self._on_iteration_completed() self._start_alarm() def _start_alarm(self): self._stop_alarm() if not self._sims_running_interaction: return next_hour = interval_in_sim_hours(int(self._total_time_ran.in_hours()) + 1) time_till_completion = (next_hour - self._total_time_ran)/len(self._sims_running_interaction) self._alarm_handle = alarms.add_alarm(self, time_till_completion, self._on_hour_reached) self._last_started_time = services.time_service().sim_now def _stop_alarm(self): if self._alarm_handle is not None: alarms.cancel_alarm(self._alarm_handle) self._alarm_handle = None self._last_started_time = None def _run_goal_completion_tests(self, sim_info, event, resolver): if not resolver(self._goal_test): return False self._stop_alarm() if event == event_testing.test_events.TestEvent.InteractionStart: self._sims_running_interaction.add(sim_info.id) else: self._sims_running_interaction.discard(sim_info.id) self._start_alarm() return False @property def completed_iterations(self): return int(self._total_time_ran.in_hours()) @property def max_iterations(self): return self._goal_test.duration
class SituationGoalInteractionTime(SituationGoal): __qualname__ = 'SituationGoalInteractionTime' DURATION_RUN = 'duration_run' REMOVE_INSTANCE_TUNABLES = ('_post_tests', ) INSTANCE_TUNABLES = { '_goal_test': TunableInteractionOfInterest( description= '\n Interaction and duration that this situation goal will use.\n Example: Bartend for 10 sim minutes.\n ', tuning_group=GroupNames.TESTS) } def __init__(self, *args, reader=None, **kwargs): super().__init__(reader=reader, *args, **kwargs) self._total_time_ran = TimeSpan.ZERO self._last_started_time = None self._alarm_handle = None self._total_duration = interval_in_sim_hours(self._goal_test.duration) self._test_events = set() self._test_events.add( event_testing.test_events.TestEvent.InteractionStart) self._test_events.add( event_testing.test_events.TestEvent.InteractionComplete) services.get_event_manager().register(self, self._test_events) if reader is not None: duration_run = reader.read_uint64(self.DURATION_RUN, 0) self._total_time_ran = TimeSpan(duration_run) self._sims_running_interaction = set() if self._situation is None and self._sim_info is not None: self._sims_running_interaction.add(self._actor_ref().id) else: for sim in self._situation.all_sims_in_situation_gen(): while sim.si_state.is_running_affordance( self._goal_test.affordance): self._sims_running_interaction.add(sim.id) if self._sims_running_interaction: self._start_alarm() def create_seedling(self): if self._alarm_handle is not None: self._start_alarm() seedling = super().create_seedling() writer = seedling.writer writer.write_uint64(self.DURATION_RUN, self._total_time_ran.in_ticks()) return seedling def decommision(self): services.get_event_manager().unregister(self, self._test_events) self._stop_alarm() super().decommision() def _on_hour_reached(self, alarm_handle=None): self._stop_alarm() if self._total_time_ran >= self._total_duration: super()._on_goal_completed() else: self._on_iteration_completed() self._start_alarm() def _start_alarm(self): self._stop_alarm() if not self._sims_running_interaction: return next_hour = interval_in_sim_hours( int(self._total_time_ran.in_hours()) + 1) time_till_completion = (next_hour - self._total_time_ran) / len( self._sims_running_interaction) self._alarm_handle = alarms.add_alarm(self, time_till_completion, self._on_hour_reached) self._last_started_time = services.time_service().sim_now def _stop_alarm(self): if self._alarm_handle is not None: alarms.cancel_alarm(self._alarm_handle) self._alarm_handle = None self._last_started_time = None def _run_goal_completion_tests(self, sim_info, event, resolver): if not resolver(self._goal_test): return False self._stop_alarm() if event == event_testing.test_events.TestEvent.InteractionStart: self._sims_running_interaction.add(sim_info.id) else: self._sims_running_interaction.discard(sim_info.id) self._start_alarm() return False @property def completed_iterations(self): return int(self._total_time_ran.in_hours()) @property def max_iterations(self): return self._goal_test.duration
def _score_and_schedule_drama_nodes_gen(self, timeline, from_zone_spin_up=False): active_household = services.active_household() if active_household is None: return current_time = services.time_service().sim_now current_day = current_time.day() venue_manager = services.get_instance_manager(sims4.resources.Types.VENUE) for neighborhood_proto in services.get_persistence_service().get_neighborhoods_proto_buf_gen(): for lot_owner_info in neighborhood_proto.lots: zone_id = lot_owner_info.zone_instance_id if not zone_id: continue venue_tuning = venue_manager.get(build_buy.get_current_venue(zone_id)) if venue_tuning is None: continue if not venue_tuning.drama_node_events: continue if is_scoring_archive_enabled(): gsi_data = GSIDramaScoringData() gsi_data.bucket = 'Venue' else: gsi_data = None yield from self.score_and_schedule_nodes_gen(venue_tuning.drama_node_events, venue_tuning.drama_node_events_to_schedule, timeline=timeline, gsi_data=gsi_data, zone_id=zone_id) if gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if timeline is not None: yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0))) bucketted_nodes = defaultdict(list) drama_node_manager = services.get_instance_manager(sims4.resources.Types.DRAMA_NODE) for drama_node in drama_node_manager.types.values(): if drama_node.scoring is None: continue bucketted_nodes[drama_node.scoring.bucket].append(drama_node) buckets_to_score = [] if from_zone_spin_up or not self._check_day(current_day, self.VENUE_BUCKET_DAYS) or from_zone_spin_up: buckets = self.STARTUP_BUCKETS - self._startup_buckets_used if current_time < create_date_and_time(days=int(current_time.absolute_days()), hours=self.SCORING_TIME): day_modifier = -1 else: day_modifier = 0 for bucket in buckets: if not bucketted_nodes[bucket]: continue self._startup_buckets_used.add(bucket) rules = self.BUCKET_SCORING_RULES[bucket] smallest_day_modification = None for (day, day_enabled) in rules.days.items(): if not day_enabled: continue potential_modification = current_day + day_modifier - day potential_modification += DAYS_PER_WEEK if not potential_modification < 0 or smallest_day_modification is None or potential_modification < smallest_day_modification: smallest_day_modification = potential_modification if smallest_day_modification is None: time_modification = TimeSpan.ZERO else: time_modification = TimeSpan(current_time.absolute_ticks()) - create_time_span(days=int(current_time.absolute_days()) - smallest_day_modification + day_modifier, hours=self.SCORING_TIME) buckets_to_score.append((bucket, rules, time_modification)) else: for (bucket_type, rules) in self.BUCKET_SCORING_RULES.items(): valid_day = self._check_day(current_day, rules.days) for drama_node in self._scheduled_nodes.values(): if drama_node.scoring is None: continue if drama_node.scoring.bucket == bucket_type: break else: valid_day = True if valid_day or not rules.score_if_no_nodes_are_scheduled or valid_day: buckets_to_score.append((bucket_type, rules, TimeSpan.ZERO)) for (bucket_type, rules, time_modifier) in buckets_to_score: if is_scoring_archive_enabled(): gsi_data = GSIDramaScoringData() gsi_data.bucket = bucket_type else: gsi_data = None if rules.number_to_schedule.option == NodeSelectionOption.BASED_ON_HOUSEHOLD: nodes_to_schedule = 1 + math.floor(len(active_household)/2) elif rules.number_to_schedule.option == NodeSelectionOption.STATIC_AMOUNT: nodes_to_schedule = rules.number_to_schedule.number_of_nodes else: logger.error('Trying to determine how many nodes to run with invalid option {}', rules.number_to_schedule.option) if gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if nodes_to_schedule == 0: if gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) for node in list(self._scheduled_nodes.values()): if not not node.scoring is not None and node.scoring.bucket == bucket_type: self.cancel_scheduled_node(node.uid) yield from self.score_and_schedule_nodes_gen(bucketted_nodes[bucket_type], nodes_to_schedule, time_modifier=time_modifier, timeline=timeline, gsi_data=gsi_data) if not rules.refresh_nodes_on_scheduling or gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if timeline is not None: yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0))) else: gsi_data.nodes_to_schedule = nodes_to_schedule for node in list(self._scheduled_nodes.values()): if not not node.scoring is not None and node.scoring.bucket == bucket_type: self.cancel_scheduled_node(node.uid) yield from self.score_and_schedule_nodes_gen(bucketted_nodes[bucket_type], nodes_to_schedule, time_modifier=time_modifier, timeline=timeline, gsi_data=gsi_data) if not rules.refresh_nodes_on_scheduling or gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if timeline is not None: yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0))) if nodes_to_schedule == 0: if gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) for node in list(self._scheduled_nodes.values()): if not not node.scoring is not None and node.scoring.bucket == bucket_type: self.cancel_scheduled_node(node.uid) yield from self.score_and_schedule_nodes_gen(bucketted_nodes[bucket_type], nodes_to_schedule, time_modifier=time_modifier, timeline=timeline, gsi_data=gsi_data) if not rules.refresh_nodes_on_scheduling or gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if timeline is not None: yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0))) else: gsi_data.nodes_to_schedule = nodes_to_schedule for node in list(self._scheduled_nodes.values()): if not not node.scoring is not None and node.scoring.bucket == bucket_type: self.cancel_scheduled_node(node.uid) yield from self.score_and_schedule_nodes_gen(bucketted_nodes[bucket_type], nodes_to_schedule, time_modifier=time_modifier, timeline=timeline, gsi_data=gsi_data) if not rules.refresh_nodes_on_scheduling or gsi_data is not None: archive_drama_scheduler_scoring(gsi_data) if timeline is not None: yield timeline.run_child(elements.SleepElement(date_and_time.TimeSpan(0)))