def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._picker_dialogs = {}
     self._text_listeners = CallableList()
     self.existing_text = None
     self.ingredient_check = None
     self._changes_made = False
 def __init__(self, sim_info):
     self._sim_info = sim_info
     self._current_away_action = None
     self._on_away_action_started = CallableList()
     self._on_away_action_ended = CallableList()
     self.add_on_away_action_started_callback(self._resend_away_action)
     self.add_on_away_action_ended_callback(self._resend_away_action)
Esempio n. 3
0
 def __init__(self, owner, resolver=None, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._owner = owner.ref()
     self._resolver = resolver
     self._additional_responses = {}
     self.response = None
     self._timestamp = None
     self._listeners = CallableList()
 def __init__(self, sim_info):
     super().__init__()
     self._sim_info = sim_info
     self._relationships = {}
     self._suppress_client_updates = False
     self.spouse_sim_id = None
     self._relationship_multipliers = {}
     self._create_relationship_callbacks = CallableList()
Esempio n. 5
0
 def __init__(self, lot_id, zone_id, routing_surface=None):
     self.center = None
     self.lot_id = lot_id
     self._tags = set()
     if routing_surface is None:
         routing_surface = routing.SurfaceIdentifier(
             zone_id, 0, routing.SURFACETYPE_WORLD)
     self._routing_surface = routing_surface
     self._valid_slots = 0
     self._on_spawn_points_changed = CallableList()
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._crafting_cache = CraftingObjectCache()
     self._sim_spawn_conditions = collections.defaultdict(set)
     self._client_connect_callbacks = CallableList()
     self._portal_cache = WeakSet()
     self._portal_added_callbacks = CallableList()
     self._portal_removed_callbacks = CallableList()
     self._front_door_candidates_changed_callback = CallableList()
     self._all_bed_tags = self.BED_TAGS.beds | self.BED_TAGS.double_beds | self.BED_TAGS.kid_beds | self.BED_TAGS.other_sleeping_spots
 def __init__(self, path, type_enum, use_guid_for_ref=False):
     self._tuned_classes = {}
     self._callback_helper = {}
     self._verify_tunable_callback_helper = {}
     self._class_templates = []
     self.PATH = path
     self.TYPE = type_enum
     self._load_all_complete = False
     self._load_all_complete_callbacks = CallableList()
     self._use_guid_for_ref = use_guid_for_ref
Esempio n. 8
0
class SpawnPoint:
    __qualname__ = 'SpawnPoint'
    ARRIVAL_SPAWN_POINT_TAG = TunableEnumEntry(description='\n        The Tag associated with Spawn Points at the front of the lot.\n        ', tunable_type=Tag, default=Tag.INVALID)
    VISITOR_ARRIVAL_SPAWN_POINT_TAG = TunableEnumEntry(description='\n        The Tag associated with Spawn Points nearby the lot for visitors.\n        ', tunable_type=Tag, default=Tag.INVALID)

    def __init__(self, lot_id, zone_id, routing_surface=None):
        self.center = None
        self.lot_id = lot_id
        self._tags = set()
        if routing_surface is None:
            routing_surface = routing.SurfaceIdentifier(zone_id, 0, routing.SURFACETYPE_WORLD)
        self._routing_surface = routing_surface
        self._valid_slots = 0
        self._on_spawn_points_changed = CallableList()

    def __str__(self):
        return 'Name:{:20} Lot:{:15} Center:{:45} Tags:{}'.format(self.get_name(), self.lot_id, self.center, self.get_tags())

    @property
    def routing_surface(self):
        return self._routing_surface

    def get_name(self):
        raise NotImplementedError

    def get_tags(self):
        return self._tags

    def has_tag(self, tag):
        if tag is not None:
            return tag in self.get_tags()
        return False

    def next_spawn_spot(self):
        raise NotImplementedError

    def get_slot_pos(self, index=None):
        raise NotImplementedError

    @property
    def valid_slots(self):
        return self._valid_slots

    def reset_valid_slots(self):
        self._valid_slots = 0

    def register_spawn_point_changed_callback(self, callback):
        self._on_spawn_points_changed.append(callback)

    def unregister_spawn_point_changed_callback(self, callback):
        self._on_spawn_points_changed.remove(callback)

    def get_slot_positions(self):
        raise NotImplementedError
Esempio n. 9
0
 def __init__(self, owner):
     super().__init__(owner)
     self._crafting_process = None
     self._use_base_recipe = False
     self.object_mutated_listeners = CallableList()
     self._servings_statistic_tracker_handle = None
     self._quality_change_callback_added = False
     self._spoil_listener_handle = None
     self._is_final_product = False
     self._last_spoiled_time = None
     self._spoil_timer_state_value = CraftingTuning.SPOILED_STATE_VALUE
Esempio n. 10
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._alarm_handle = None
     self._processing_task = None
     self._on_update_callbacks = CallableList()
     self._pending_broadcasters = []
     self._active_broadcasters = []
     self._cluster_requests = {}
     self._object_cache = None
     self._pending_update = False
     self._quadtrees = defaultdict(sims4.geometry.QuadTree)
Esempio n. 11
0
 def __init__(self,
              sim_info=None,
              situation=None,
              goal_id=0,
              count=0,
              **kwargs):
     self._sim_info = sim_info
     self._situation = situation
     self.id = goal_id
     self._on_goal_completed_callbacks = CallableList()
     self._completed_time = None
     self._count = count
Esempio n. 12
0
class UiDialogBase:

	def __init__ (self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self.response = None
		self._listeners = CallableList()

	def add_listener (self, listener_callback):
		self._listeners.append(listener_callback)

	def distribute_dialog (self, dialog_type, dialog_msg, immediate = False):
		distributor = Distributor.instance()
		distributor.add_event(dialog_type, dialog_msg, immediate = immediate)

	def get_phone_ring_type (self):
		return PhoneRingType.NO_RING

	@property
	def responses (self):
		return tuple()

	def has_responses (self):
		return self.responses or self._additional_responses

	def on_response_received (self):
		pass

	def respond (self, response: int) -> bool:
		try:
			self.response = response
			self._listeners(self)
			return True
		finally:
			self.on_response_received()
		return False

	def show_dialog (self, on_response = None, **kwargs):
		if on_response is not None:
			self.add_listener(on_response)
		pythonutils.try_highwater_gc()
		services.ui_dialog_service().dialog_show(self, self.get_phone_ring_type(), **kwargs)

	def do_auto_respond (self, auto_response = DEFAULT):
		if auto_response is not DEFAULT:
			response = auto_response
		elif ButtonType.DIALOG_RESPONSE_CANCEL in self.responses:
			response = ButtonType.DIALOG_RESPONSE_CANCEL
		elif ButtonType.DIALOG_RESPONSE_OK in self.responses:
			response = ButtonType.DIALOG_RESPONSE_OK
		else:
			response = ButtonType.DIALOG_RESPONSE_CLOSED
		services.ui_dialog_service().dialog_respond(self.dialog_id, response)
Esempio n. 13
0
 def __init__(self, path, type_enum, use_guid_for_ref=False, base_game_only=False, require_reference=False):
     self._tuned_classes = {}
     self._remapped_keys = {}
     self._callback_helper = {}
     self._verify_tunable_callback_helper = {}
     self._class_templates = []
     self.PATH = path
     self.TYPE = type_enum
     self._load_all_complete = False
     self._load_all_complete_callbacks = CallableList()
     self._use_guid_for_ref = use_guid_for_ref
     self._base_game_only = base_game_only
     self._require_reference = require_reference
Esempio n. 14
0
 def __init__(self):
     super().__init__()
     self._save_locks = []
     self._read_write_locked = False
     self._save_game_data_proto = serialization.SaveGameData()
     self.save_timeline = None
     self._unlocked_callbacks = CallableList()
     self.once_per_session_telemetry_sent = False
     self.save_error_code = persistence_error_types.ErrorCodes.NO_ERROR
     self._zone_data_pb_cache = {}
     self._world_id_to_region_id_cache = {}
     self._sim_data_pb_cache = None
     self._household_pb_cache = None
     self._world_ids = frozenset()
Esempio n. 15
0
 def __init__(self, zone_id, save_slot_data_id):
     self.id = zone_id
     self.neighborhood_id = 0
     self.open_street_id = 0
     self.lot = Lot(zone_id)
     self.entitlement_unlock_handlers = {}
     self._spawner_data = {}
     self._dynamic_spawn_points = {}
     self._zone_state = zone_types.ZoneState.ZONE_INIT
     self._zone_state_callbacks = {}
     self.all_transition_controllers = weakref.WeakSet()
     self.navmesh_change_callbacks = CallableListPreventingRecursion()
     self.wall_contour_update_callbacks = CallableListPreventingRecursion()
     self.foundation_and_level_height_update_callbacks = CallableListPreventingRecursion(
     )
     self.navmesh_id = None
     self.object_count = 0
     self.is_in_build_buy = False
     self.objects_to_fixup_post_bb = None
     self._save_slot_data_id = save_slot_data_id
     self._royalty_alarm_manager = RoyaltyAlarmManager()
     self.current_navmesh_fence_id = 1
     self._first_visit_to_zone = None
     self._active_lot_arrival_spawn_point = None
     self._time_of_last_open_street_save = None
     for key in zone_types.ZoneState:
         while key != zone_types.ZoneState.ZONE_INIT:
             self._zone_state_callbacks[key] = CallableList()
     self._client = None
     self._tick_metrics = None
Esempio n. 16
0
 def __init__(self, sim_info=None, situation=None, goal_id=0, count=0, **kwargs):
     self._sim_info = sim_info
     self._situation = situation
     self.id = goal_id
     self._on_goal_completed_callbacks = CallableList()
     self._completed_time = None
     self._count = count
Esempio n. 17
0
 def __init__(self, owner, resolver=None, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._owner = owner.ref()
     self._resolver = resolver
     self._additional_responses = {}
     self.response = None
     self._timestamp = None
     self._listeners = CallableList()
Esempio n. 18
0
 def __init__(self, sim):
     self._super_interactions = set()
     self._removing_interactions = set()
     self._resetting = False
     self._sim = sim.ref() if sim else None
     self._watchers = {}
     self._constraints = {}
     self.on_changed = CallableList()
Esempio n. 19
0
 def __init__(self, *args, manager_id=0, **kwargs):
     super().__init__(*args, **kwargs)
     self.id = manager_id
     self._objects = {}
     self._objects_to_be_removed = []
     self._registered_callbacks = {}
     for key in CallbackTypes:
         self._registered_callbacks[key] = CallableList()
 def _register_home_street_change_callback(callbacks, street, callback):
     if street is None or callback is None:
         logger.error(
             'Attempt to register an incomplete callback: {} for street {}',
             callback, street)
         return
     if street not in callbacks:
         callbacks[street] = CallableList()
     callbacks[street].append(callback)
Esempio n. 21
0
 def __init__(self, owner):
     super().__init__(owner)
     self._crafting_process = None
     self._use_base_recipe = False
     self.object_mutated_listeners = CallableList()
     self._servings_statistic_tracker_handle = None
     self._quality_change_callback_added = False
     self._spoil_listener_handle = None
     self._is_final_product = False
Esempio n. 22
0
 def __init__(self, lot_id, zone_id, routing_surface=None):
     self.center = None
     self.lot_id = lot_id
     self._tags = set()
     if routing_surface is None:
         routing_surface = routing.SurfaceIdentifier(zone_id, 0, routing.SURFACETYPE_WORLD)
     self._routing_surface = routing_surface
     self._valid_slots = 0
     self._on_spawn_points_changed = CallableList()
Esempio n. 23
0
 def __init__(self, session_id, account, household_id):
     self.id = session_id
     self.manager = None
     self._account = account
     self._household_id = household_id
     self._choice_menu = None
     self._interaction_parameters = {}
     self.active = True
     self.zone_id = sims4.zone_utils.get_zone_id()
     self._selectable_sims = SelectableSims(self)
     self._active_sim_info = None
     self._active_sim_changed = CallableList()
     self.ui_objects = weakref.WeakSet()
     self.primitives = ()
     self._live_drag_objects = []
     self._live_drag_start_system = LiveDragLocation.INVALID
     self._live_drag_is_stack = False
     self._live_drag_sell_dialog_active = False
Esempio n. 24
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._zone_curfew_data = {}
     self._curfew_warning_alarm_handle = None
     self._curfew_started_alarm_handle = None
     self._curfew_ended_alarm_handle = None
     self._curfew_message_alarm_handle = None
     self._curfew_warning_callback = CallableList()
     self._curfew_started_callback = CallableList()
     self._curfew_ended_callback = CallableList()
     self._time_set_callback = CallableList()
 def __init__(self, path, type_enum, use_guid_for_ref=False):
     self._tuned_classes = {}
     self._callback_helper = {}
     self._verify_tunable_callback_helper = {}
     self._class_templates = []
     self.PATH = path
     self.TYPE = type_enum
     self._load_all_complete = False
     self._load_all_complete_callbacks = CallableList()
     self._use_guid_for_ref = use_guid_for_ref
Esempio n. 26
0
 def detach(self, *detaching_objects):
     global _nested_arb_detach_callbacks
     if self.master not in detaching_objects:
         for detaching_object in detaching_objects:
             self._attached_actors.remove(detaching_object)
         super().detach(*detaching_objects)
         return
     if _nested_arb_depth > 0:
         if _nested_arb_detach_callbacks is None:
             _nested_arb_detach_callbacks = CallableList()
         super_self = super()
         _nested_arb_detach_callbacks.append(
             lambda: super_self.detach(*self._attached_actors))
         return True
     if _nested_arb_detach_callbacks is not None:
         cl = _nested_arb_detach_callbacks
         _nested_arb_detach_callbacks = None
         cl()
     super().detach(*self._attached_actors)
 def register_sim_removed_callback(self, street, callback):
     if street is None or callback is None:
         logger.error(
             'Attempted to register an incomplete callback: {} for street {}',
             callback, street)
         return
     if street not in self._street_sim_removed_callbacks:
         self._street_sim_removed_callbacks[street] = CallableList()
     callback_list = self._street_sim_removed_callbacks[street]
     if callback not in callback_list:
         callback_list.register(callback)
Esempio n. 28
0
 def __init__(self,
              sim_info=None,
              situation=None,
              goal_id=0,
              count=0,
              locked=False,
              completed_time=None,
              secondary_sim_info=None,
              **kwargs):
     self._sim_info = sim_info
     self._secondary_sim_info = secondary_sim_info
     self._situation = situation
     self.id = goal_id
     self._on_goal_completed_callbacks = CallableList()
     self._completed_time = completed_time
     self._count = count
     self._locked = locked
     self._score_override = None
     self._goal_status_override = None
     self._setup = False
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._alarm_handle = None
     self._processing_task = None
     self._on_update_callbacks = CallableList()
     self._pending_broadcasters = []
     self._active_broadcasters = []
     self._cluster_requests = {}
     self._object_cache = None
     self._pending_update = False
     self._quadtrees = defaultdict(sims4.geometry.QuadTree)
Esempio n. 30
0
 def __init__(self, asm_key, context, posture_manifest_overrides=None):
     super().__init__(asm_key)
     self.context = context
     self._posture_manifest_overrides = posture_manifest_overrides
     self._prop_overrides = {}
     self._prop_state_values = {}
     self._vfx_overrides = {}
     self._sound_overrides = {}
     self._actors = {}
     self._virtual_actors = defaultdict(set)
     self._virtual_actor_relationships = {}
     self._on_state_changed_events = CallableList()
Esempio n. 31
0
 def __init__(self, owner):
     super().__init__(owner)
     self._active_buffs = {}
     self._get_next_handle_id = UniqueIdGenerator()
     self._success_chance_modification = 0
     self._active_mood = self.DEFAULT_MOOD
     self._active_mood_intensity = 0
     self._active_mood_buff_handle = None
     self.on_mood_changed = CallableList()
     self.on_mood_changed.append(self._publish_mood_update)
     self.on_mood_changed.append(self._send_mood_changed_event)
     self.load_in_progress = False
     self.on_buff_added = CallableList()
     self.on_buff_removed = CallableList()
     self.buff_update_alarms = {}
     if self._active_mood is None:
         logger.error('No default mood tuned in buff_component.py')
     elif self._active_mood.buffs:
         initial_buff_ref = self._active_mood.buffs[0]
         if initial_buff_ref and initial_buff_ref.buff_type:
             self._active_mood_buff_handle = self.add_buff(initial_buff_ref.buff_type)
Esempio n. 32
0
 def __init__(self, owner, **kwargs):
     super().__init__(owner, **kwargs)
     self.owner = owner
     walkstyle_behavior = self.get_walkstyle_behavior()
     self._walkstyle_requests = [WalkStyleRequest(self.owner, walkstyle=walkstyle_behavior.default_walkstyle, priority=-1)]
     self._walk_style_handles = {}
     self.wading_buff_handle = None
     self.last_route_has_wading_nodes = False
     self._routing_stage_event_callbacks = defaultdict(CallableList)
     if owner.is_sim:
         owner.remove_component(objects.components.types.FOOTPRINT_COMPONENT)
     self._path_plan_context_map = {}
     self.on_slot = None
     self.stand_slot_reservation_removed_callbacks = CallableList()
     self._active_follow_path_weakref = None
     self.on_follow_path = CallableList()
     self.on_plan_path = CallableList()
     self.on_intended_location_changed = CallableList()
     self._current_path = None
     self._routing_slave_data = []
     self._routing_master_ref = None
     self._default_agent_radius = None
     self._route_event_context = RouteEventContext()
     self._route_interaction = None
     self._animation_context = None
     self._initial_carry_targets = None
     self._route_event_provider_requests = None
Esempio n. 33
0
 def __init__(self,
              *args,
              sim_id: int = 0,
              gender: Gender = Gender.MALE,
              age: Age = Age.ADULT,
              species: SpeciesExtended = SpeciesExtended.HUMAN,
              first_name: str = '',
              last_name: str = '',
              breed_name: str = '',
              full_name_key=0,
              breed_name_key=0,
              physique: str = '',
              skin_tone=1,
              **kwargs):
     super().__init__(*args, **kwargs)
     self.sim_id = sim_id or id_generator.generate_object_id()
     self.primitives = distributor.ops.DistributionSet(self)
     self.manager = None
     self._base = BaseSimInfo(sim_id, first_name, last_name, breed_name,
                              full_name_key, breed_name_key, age, gender,
                              species, skin_tone, physique)
     self._base.voice_pitch = 0.0
     self._base.voice_actor = 0
     self._base.voice_effect = 0
     self._base.facial_attributes = ''
     self._base.custom_texture = 0
     self._base.pelt_layers = ''
     self._species = SpeciesExtended.get_species(species)
     self.on_base_characteristic_changed = CallableList()
     self.on_outfit_changed = CallableList()
     self.on_outfit_generated = CallableList()
     self._set_current_outfit_without_distribution(
         (OutfitCategory.EVERYDAY, 0))
     self._previous_outfit = (OutfitCategory.EVERYDAY, 0)
     self._preload_outfit_list = []
     self.on_preload_outfits_changed = CallableList()
     self.appearance_tracker = AppearanceTracker(self)
     self.visible_to_client = False
Esempio n. 34
0
 def __init__(self, asm_key, context, posture_manifest_overrides=None):
     super().__init__(asm_key)
     self.context = context
     self._posture_manifest_overrides = posture_manifest_overrides
     self._prop_overrides = {}
     self._alt_prop_definitions = {}
     self._prop_state_values = {}
     self._vfx_overrides = {}
     self._sound_overrides = {}
     self._actors = {}
     self._virtual_actors = defaultdict(set)
     self._virtual_actor_relationships = {}
     self._on_state_changed_events = CallableList()
     self._boundary_condition_dirty = asm_key in sims4.resources.localwork_no_groupid
Esempio n. 35
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._sim_infos_saved_in_zone = []
     self._sim_infos_saved_in_plex_group = []
     self._sim_infos_saved_in_open_street = []
     self._sims_traveled_to_zone = []
     self._sim_infos_injected_into_zone = []
     self._sim_info_to_spin_up_action = None
     self._startup_time = None
     self._sim_ids_to_skip_preroll = set()
     self.on_sim_info_removed = CallableList()
     self._firemeter = None
     self._sim_info_telemetry_manager = SimInfoTelemetryManager()
     self._start_all_sims_opted_out_of_fame = False
     self._sim_info_cap_override = None
Esempio n. 36
0
 def __init__(self,
              sim_id,
              rabbit_hole_id=None,
              starting_phase=RabbitHolePhase.STARTING,
              picked_skill=None):
     self.rabbit_hole_id = rabbit_hole_id or id_generator.generate_object_id(
     )
     self.sim_id = sim_id
     self.alarm_handle = None
     self.callbacks = CallableList()
     self.linked_rabbit_holes = []
     self.picked_skill = picked_skill
     self.ignore_travel_cancel_callbacks = False
     self.current_phase = starting_phase
     self._selected_affordance = None
     self.time_remaining_on_load = None
Esempio n. 37
0
 def __init__(self):
     self._persisted_background_event_id = None
     self._persisted_special_event_id = None
     self._special_event_start_alarm = None
     self._source_venue = None
     self._active_venue = None
     self._zone_director = None
     self._requested_zone_directors = []
     self._prior_zone_director_proto = None
     self._open_street_director_requests = []
     self._prior_open_street_director_proto = None
     self.build_buy_edit_mode = False
     self.on_venue_type_changed = CallableList()
     self._venue_start_time = None
     self._university_housing_household_validation_alarm = None
     self._university_housing_kick_out_completed = False
Esempio n. 38
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._crafting_cache = CraftingObjectCache()
     self._sim_spawn_conditions = collections.defaultdict(set)
     self._water_terrain_object_cache = WaterTerrainObjectCache()
     self._client_connect_callbacks = CallableList()
     self._portal_cache = WeakSet()
     self._portal_added_callbacks = CallableList()
     self._portal_removed_callbacks = CallableList()
     self._front_door_candidates_changed_callback = CallableList()
     self._all_bed_tags = self.BED_TAGS.beds | self.BED_TAGS.double_beds | self.BED_TAGS.kid_beds | self.BED_TAGS.other_sleeping_spots
     self._tag_to_object_list = defaultdict(set)
     self._whim_set_cache = Counter()
     self._posture_providing_object_cache = None
     self._objects_to_ignore_portal_validation_cache = []
Esempio n. 39
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._reservations = WeakKeyDictionary()
     self._reservations_multi = WeakKeyDictionary()
     self._use_list_changed_callbacks = CallableList()
Esempio n. 40
0
class UseListMixin:
    __qualname__ = 'UseListMixin'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._reservations = WeakKeyDictionary()
        self._reservations_multi = WeakKeyDictionary()
        self._use_list_changed_callbacks = CallableList()

    @property
    def using_sim(self):
        if not self._reservations:
            return
        return next(self._reservations.keys())

    @property
    def in_use(self):
        if self._reservations:
            return True
        return False

    def in_use_by(self, sim, owner=None):
        owners = self._reservations.get(sim)
        if not owners:
            return False
        return owner is None or owner in owners

    def _may_reserve_obj(self, sim, affordance, context):
        if self.in_use_by(sim):
            return True
        if self.children:
            for child in self.children:
                while not child.may_reserve(sim, multi=False, affordance=affordance, context=context):
                    return False
        return not self._reservations

    def _may_reserve_part(self, sim, affordance, context, check_overlapping_parts=True):
        part_list = self.part_owner.parts
        for part in part_list:
            if part is self:
                pass
            using_sim = part.using_sim
            while not using_sim is None:
                if using_sim is sim:
                    pass
                for si in using_sim.si_state:
                    reserve_object_tests = si.object_reservation_tests
                    while reserve_object_tests:
                        reserve_result = reserve_object_tests.run_tests(si.get_resolver(target=sim))
                        if not reserve_result:
                            return reserve_result
                if using_sim.queue.transition_controller is not None:
                    transitioning_interaction = using_sim.queue.transition_controller.interaction
                    if transitioning_interaction.is_super:
                        reserve_object_tests = transitioning_interaction.object_reservation_tests
                        if reserve_object_tests:
                            target_sim = sim if transitioning_interaction.sim is not sim else using_sim
                            reserve_result = reserve_object_tests.run_tests(transitioning_interaction.get_resolver(target=target_sim))
                            if not reserve_result:
                                return reserve_result
                while affordance is not None and affordance.is_super:
                    if context is None:
                        logger.error('Attempt to call may_reserve() with an affordance but no context!', owner='maxr')
                    reserve_object_tests = affordance.object_reservation_tests
                    if reserve_object_tests:
                        reserve_result = reserve_object_tests.run_tests(affordance.get_resolver(target=using_sim, context=context))
                        if not reserve_result:
                            return reserve_result
        if affordance is not None and not self.supports_affordance(affordance):
            return TestResult(False, '{} does not support {}'.format(self, affordance))
        reserve_result = self._may_reserve_obj(sim, affordance=affordance, context=context)
        if not reserve_result:
            return reserve_result
        if check_overlapping_parts:
            for overlapping_part in self.get_overlapping_parts():
                if overlapping_part is self:
                    pass
                reserve_result = overlapping_part._may_reserve_part(sim, None, None, check_overlapping_parts=False)
                while not reserve_result:
                    return reserve_result
        return TestResult(True, 'Passed all reservation tests.')

    def may_reserve(self, sim, multi=False, affordance=None, context=None):
        if self.parts:
            logger.callstack('Cannot reserve an object that has parts: {}, for {} running {} - multi: {}', self, sim, affordance, multi, level=sims4.log.LEVEL_ERROR)
            return False
        if multi:
            return True
        if self.is_part:
            result = self._may_reserve_part(sim, affordance, context)
        else:
            result = self._may_reserve_obj(sim, affordance, context)
        return result

    def reserve(self, sim, owner, multi=False):
        use_list = self._reservations_multi if multi else self._reservations
        sim_list = setdefault_callable(use_list, sim, WeakSet)
        sim_list.add(owner)
        self._use_list_changed_callbacks(user=sim, added=True)

    def release(self, sim, owner, multi=False):
        use_list = self._reservations_multi if multi else self._reservations
        sim_list = use_list.get(sim)
        sim_list.remove(owner)
        if not sim_list:
            del use_list[sim]
        self._use_list_changed_callbacks(user=sim, added=False)
        self._destroy_if_necessary()

    def make_transient(self):
        self.transient = True
        self._destroy_if_necessary()

    def _destroy_if_necessary(self):
        if not self._reservations_multi and not self._reservations and self.transient:
            if self.is_part:
                self.part_owner.schedule_destroy_asap(source=self, cause='Destroying unused transient part.')
            else:
                self.schedule_destroy_asap(source=self, cause='Destroying unused transient object.')

    def usable_by_transition_controller(self, transition_controller):
        if transition_controller is not None:
            required_sims = transition_controller.interaction.required_sims()
            if self.is_part:
                all_overlapping_parts = self.get_overlapping_parts()
                all_overlapping_parts.append(self)
                for part in all_overlapping_parts:
                    for user in part.get_users():
                        while user not in required_sims:
                            return False
                return True
            for required_sim in required_sims:
                while self.in_use_by(required_sim):
                    return True
        return False

    def get_users(self, sims_only=False, include_multi=True):
        targets = (self,) if not self.parts else self.parts
        if include_multi:
            return {sim for target in targets for sim in itertools.chain(target._reservations, target._reservations_multi) if not sims_only or sim.is_sim}
        return {sim for target in targets for sim in target._reservations if not sims_only or sim.is_sim}

    def on_reset_get_interdependent_reset_records(self, reset_reason, reset_records):
        super().on_reset_get_interdependent_reset_records(reset_reason, reset_records)
        relevant_sims = self.get_users(sims_only=True)
        for sim in relevant_sims:
            if self.reset_reason() == ResetReason.BEING_DESTROYED:
                reset_records.append(ResetRecord(sim, ResetReason.RESET_EXPECTED, self, 'In use list of object being destroyed.'))
            body_target_part_owner = sim.posture_state.body.target
            if body_target_part_owner is not None and body_target_part_owner.is_part:
                body_target_part_owner = body_target_part_owner.part_owner
            transition_controller = sim.queue.transition_controller
            while body_target_part_owner is self or transition_controller is None or not transition_controller.will_derail_if_given_object_is_reset(self):
                reset_records.append(ResetRecord(sim, ResetReason.RESET_EXPECTED, self, 'Transitioning To or In.'))

    def register_on_use_list_changed(self, callback):
        self._use_list_changed_callbacks.append(callback)

    def unregister_on_use_list_changed(self, callback):
        if callback in self._use_list_changed_callbacks:
            self._use_list_changed_callbacks.remove(callback)

    def _print_in_use(self):
        if self._reservations:
            for sim in self._reservations:
                logger.debug('    Reservation: {}', sim)
        if self._reservations_multi:
            for sim in self._reservations_multi:
                logger.debug('    Reservation_Multi: {}', sim)
Esempio n. 41
0
class UiDialog(HasTunableFactory, AutoFactoryInit):
    __qualname__ = 'UiDialog'
    DIALOG_MSG_TYPE = Consts_pb2.MSG_UI_DIALOG_SHOW
    FACTORY_TUNABLES = {'title': OptionalTunable(description='\n            If enabled, this dialog will include title text.\n            ', tunable=TunableLocalizedStringFactory(description="\n                The dialog's title.\n                ")), 'text': TunableLocalizedStringFactoryVariant(description="\n            The dialog's text.\n            "), 'text_tokens': OptionalTunable(description='\n            If enabled, define text tokens to be used to localized text.\n            ', tunable=LocalizationTokens.TunableFactory(description='\n                Define the text tokens that are available to all text fields in\n                the dialog, such as title, text, responses, default and initial\n                text values, tooltips, etc.\n                '), disabled_value=DEFAULT), 'icon': OptionalTunable(description='\n            If enabled, specify an icon to be displayed.\n            ', tunable=TunableIconVariant(), needs_tuning=True), 'secondary_icon': OptionalTunable(description='\n            If enabled, specify a secondary icon to be displayed. Only certain\n            dialog types may support this field.\n            ', tunable=TunableIconVariant(), needs_tuning=True), 'phone_ring_type': TunableEnumEntry(description='\n             The phone ring type of this dialog.  If tuned to anything other\n             than None this dialog will only appear after clicking on the phone.\n             ', tunable_type=PhoneRingType, needs_tuning=True, default=PhoneRingType.NO_RING), 'audio_sting': OptionalTunable(description='\n            If enabled, play an audio sting when the dialog is shown.\n            ', tunable=TunablePlayAudio()), 'ui_responses': TunableList(description='\n            A list of buttons that are mapped to UI commands.\n            ', tunable=get_defualt_ui_dialog_response()), 'dialog_options': TunableEnumFlags(description='\n            Options to apply to the dialog.\n            ', enum_type=UiDialogOption, allow_no_flags=True, default=UiDialogOption.DISABLE_CLOSE_BUTTON)}

    def __init__(self, owner, resolver=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._owner = owner.ref()
        self._resolver = resolver
        self._additional_responses = {}
        self.response = None
        self._timestamp = None
        self._listeners = CallableList()

    @property
    def accepted(self) -> bool:
        return self.response is not None and self.response != ButtonType.DIALOG_RESPONSE_CLOSED

    @property
    def responses(self):
        return tuple()

    @property
    def owner(self):
        return self._owner()

    @property
    def dialog_type(self):
        return self._dialog_type

    def add_listener(self, listener_callback):
        self._listeners.append(listener_callback)

    def set_responses(self, responses):
        self._additional_responses = tuple(responses)

    def has_responses(self):
        return self.responses or self._additional_responses

    def _get_responses_gen(self):
        yield self.responses
        yield self._additional_responses
        yield self.ui_responses

    def respond(self, response) -> bool:
        try:
            self.response = response
            self._listeners(self)
            return True
        finally:
            self.on_response_received()
        return False

    def update(self) -> bool:
        return True

    def show_dialog(self, on_response=None, **kwargs):
        if self.audio_sting is not None:
            play_tunable_audio(self.audio_sting, None)
        if on_response is not None:
            self.add_listener(on_response)
        pythonutils.try_highwater_gc()
        services.ui_dialog_service().dialog_show(self, self.phone_ring_type, **kwargs)

    def distribute_dialog(self, dialog_type, dialog_msg):
        distributor = Distributor.instance()
        distributor.add_event(dialog_type, dialog_msg)

    def _build_localized_string_msg(self, string, *additional_tokens):
        if string is None:
            logger.callstack('_build_localized_string_msg received None for the string to build. This is probably not intended.', owner='tingyul')
            return
        tokens = ()
        if self._resolver is not None:
            if self.text_tokens is DEFAULT:
                tokens = self._resolver.get_localization_tokens()
            elif self.text_tokens is not None:
                tokens = self.text_tokens.get_tokens(self._resolver)
        return string(*tokens + additional_tokens)

    def _build_response_arg(self, response, response_msg, tutorial_id=None, additional_tokens=(), **kwargs):
        response_msg.choice_id = response.dialog_response_id
        response_msg.ui_request = response.ui_request
        if response.text is not None:
            response_msg.text = self._build_localized_string_msg(response.text, *additional_tokens)
        if tutorial_id is not None:
            response_msg.tutorial_args.tutorial_id = tutorial_id

    def build_msg(self, additional_tokens=(), icon_override=DEFAULT, secondary_icon_override=DEFAULT, **kwargs):
        msg = Dialog_pb2.UiDialogMessage()
        msg.dialog_id = self.dialog_id
        msg.owner_id = self.owner.id
        msg.dialog_type = Dialog_pb2.UiDialogMessage.DEFAULT
        if self.title is not None:
            msg.title = self._build_localized_string_msg(self.title, *additional_tokens)
        msg.text = self._build_localized_string_msg(self.text, *additional_tokens)
        if icon_override is DEFAULT:
            if self.icon is not None:
                icon_info = self.icon(self._resolver)
                key = icon_info[0]
                if key is not None:
                    msg.icon.type = key.type
                    msg.icon.group = key.group
                    msg.icon.instance = key.instance
                build_icon_info_msg(icon_info, None, msg.icon_info)
        elif icon_override is not None:
            build_icon_info_msg(icon_override, None, msg.icon_info)
        if secondary_icon_override is DEFAULT:
            if self.secondary_icon is not None:
                icon_info = self.secondary_icon(self._resolver)
                build_icon_info_msg(icon_info, None, msg.secondary_icon_info)
        elif secondary_icon_override is not None:
            build_icon_info_msg(secondary_icon_override, None, msg.secondary_icon_info)
        msg.dialog_options = self.dialog_options
        responses = []
        responses.extend(self._get_responses_gen())
        responses.sort(key=lambda response: response.sort_order)
        for response in responses:
            response_msg = msg.choices.add()
            self._build_response_arg(response, response_msg, additional_tokens=additional_tokens, **kwargs)
        return msg

    def on_response_received(self):
        pass

    def do_auto_respond(self):
        if ButtonType.DIALOG_RESPONSE_CANCEL in self.responses:
            response = ButtonType.DIALOG_RESPONSE_CANCEL
        elif ButtonType.DIALOG_RESPONSE_OK in self.responses:
            response = ButtonType.DIALOG_RESPONSE_OK
        else:
            response = ButtonType.DIALOG_RESPONSE_CLOSED
        services.ui_dialog_service().dialog_respond(self.dialog_id, response)
Esempio n. 42
0
class Client:
    __qualname__ = 'Client'
    _interaction_source = interactions.context.InteractionContext.SOURCE_PIE_MENU
    _interaction_priority = interactions.priority.Priority.High

    def __init__(self, session_id, account, household_id):
        self.id = session_id
        self.manager = None
        self._account = account
        self._household_id = household_id
        self._choice_menu = None
        self._interaction_parameters = {}
        self.active = True
        self.zone_id = sims4.zone_utils.get_zone_id()
        self._selectable_sims = SelectableSims(self)
        self._active_sim_info = None
        self._active_sim_changed = CallableList()
        self.ui_objects = weakref.WeakSet()
        self.primitives = ()
        self._live_drag_objects = []
        self._live_drag_start_system = LiveDragLocation.INVALID
        self._live_drag_is_stack = False
        self._live_drag_sell_dialog_active = False

    def __repr__(self):
        return '<Client {0:#x}>'.format(self.id)

    @property
    def account(self):
        return self._account

    @distributor.fields.Field(op=distributor.ops.UpdateClientActiveSim)
    def active_sim_info(self):
        return self._active_sim_info

    resend_active_sim_info = active_sim_info.get_resend()

    @active_sim_info.setter
    def active_sim_info(self, sim_info):
        self._set_active_sim_without_field_distribution(sim_info)

    @property
    def active_sim(self):
        if self.active_sim_info is not None:
            return self.active_sim_info.get_sim_instance(allow_hidden_flags=ALL_HIDDEN_REASONS)

    @active_sim.setter
    def active_sim(self, sim):
        self.active_sim_info = sim.sim_info

    def _set_active_sim_without_field_distribution(self, sim_info):
        if self._active_sim_info is not None and self._active_sim_info is sim_info:
            return
        current_sim = self._active_sim_info.get_sim_instance() if self._active_sim_info is not None else None
        new_sim = sim_info.get_sim_instance() if sim_info is not None else None
        if sim_info is not None:
            self._active_sim_info = sim_info
            sim_info.household.on_active_sim_changed(sim_info)
        else:
            self._active_sim_info = None
        self.notify_active_sim_changed(current_sim, new_sim)

    @property
    def choice_menu(self):
        return self._choice_menu

    @property
    def interaction_source(self):
        return self._interaction_source

    @interaction_source.setter
    def interaction_source(self, value):
        if value is None:
            del self._interaction_source
        else:
            self._interaction_source = value

    @property
    def interaction_priority(self):
        return self._interaction_priority

    @interaction_priority.setter
    def interaction_priority(self, value):
        if value is None:
            del self._interaction_priority
        else:
            self._interaction_priority = value

    @property
    def household_id(self):
        return self._household_id

    @property
    def household(self):
        household_manager = services.household_manager()
        if household_manager is not None:
            return household_manager.get(self._household_id)

    @property
    def selectable_sims(self):
        return self._selectable_sims

    def create_interaction_context(self, sim, **kwargs):
        context = interactions.context.InteractionContext(sim, self.interaction_source, self.interaction_priority, client=self, **kwargs)
        return context

    @property
    def live_drag_objects(self):
        return self._live_drag_objects

    def get_interaction_parameters(self):
        return self._interaction_parameters

    def set_interaction_parameters(self, **kwargs):
        self._interaction_parameters = kwargs

    def set_choices(self, new_choices):
        self._choice_menu = new_choices

    def select_interaction(self, choice_id, revision):
        if self.choice_menu is not None and revision == self.choice_menu.revision:
            choice_menu = self.choice_menu
            self._choice_menu = None
            self.set_interaction_parameters()
            try:
                return choice_menu.select(choice_id)
            except:
                if choice_menu.context.sim is not None:
                    choice_menu.context.sim.reset(ResetReason.RESET_ON_ERROR, cause='Exception while selecting interaction from the pie menu.')
                raise

    def get_create_op(self, *args, **kwargs):
        return distributor.ops.ClientCreate(self, is_active=True, *args, **kwargs)

    def get_delete_op(self):
        return distributor.ops.ClientDelete()

    def get_create_after_objs(self):
        active = self.active_sim
        if active is not None:
            yield active
        household = self.household
        if household is not None:
            yield household

    @property
    def valid_for_distribution(self):
        return True

    def refresh_achievement_data(self):
        active_sim_info = None
        if self.active_sim is not None:
            active_sim_info = self.active_sim.sim_info
        self.account.achievement_tracker.refresh_progress(active_sim_info)

    def send_message(self, msg_id, msg):
        if self.active:
            omega.send(self.id, msg_id, msg.SerializeToString())
        else:
            logger.warn('Message sent to client {} after it has already disconnected.', self)

    def send_serialized_message(self, msg_id, msg):
        if self.active:
            omega.send(self.id, msg_id, msg)
        else:
            logger.warn('Serialized message sent to client {} after it has already disconnected.', self)

    def set_next_sim(self):
        sim_info = self._selectable_sims.get_next_selectable(self._active_sim_info)
        if sim_info is self.active_sim_info:
            return False
        return self.set_active_sim_info(sim_info)

    def set_next_sim_or_none(self, only_if_this_active_sim_info=None):
        if only_if_this_active_sim_info is not None and self._active_sim_info is not only_if_this_active_sim_info:
            return
        sim_info = self._selectable_sims.get_next_selectable(self._active_sim_info)
        if sim_info is None:
            return self.set_active_sim_info(None)
        if sim_info is self._active_sim_info:
            return self.set_active_sim_info(None)
        return self.set_active_sim_info(sim_info)

    def set_active_sim_by_id(self, sim_id):
        if self.active_sim_info is not None and self.active_sim_info.id == sim_id:
            return False
        for sim_info in self._selectable_sims:
            while sim_info.sim_id == sim_id:
                if not sim_info.is_enabled_in_skewer:
                    return False
                return self.set_active_sim_info(sim_info)
        return False

    def set_active_sim(self, sim):
        return self.set_active_sim_info(sim.sim_info)

    def set_active_sim_info(self, sim_info):
        with telemetry_helper.begin_hook(writer, TELEMETRY_HOOK_ACTIVE_SIM_CHANGED, sim=sim_info):
            pass
        self.active_sim_info = sim_info
        return self._active_sim_info is not None

    def add_selectable_sim_info(self, sim_info, send_relationship_update=True):
        self._selectable_sims.add_selectable_sim_info(sim_info, send_relationship_update=send_relationship_update)
        if self.active_sim_info is None:
            self.set_next_sim()
        self.household.refresh_aging_updates(sim_info)

    def add_selectable_sim_by_id(self, sim_id):
        sim_info = services.sim_info_manager().get(sim_id)
        if sim_info is not None:
            self.add_selectable_sim_info(sim_info)

    def remove_selectable_sim_info(self, sim_info):
        self._selectable_sims.remove_selectable_sim_info(sim_info)
        if self.active_sim_info is None:
            self.set_next_sim()
        self.household.refresh_aging_updates(sim_info)

    def remove_selectable_sim_by_id(self, sim_id):
        if len(self._selectable_sims) <= 1:
            return False
        sim_info = services.sim_info_manager().get(sim_id)
        if sim_info is not None:
            self.remove_selectable_sim_info(sim_info)
        return True

    def make_all_sims_selectable(self):
        self.clear_selectable_sims()
        for sim_info in services.sim_info_manager().objects:
            self._selectable_sims.add_selectable_sim_info(sim_info)
        self.set_next_sim()

    def clear_selectable_sims(self):
        self.active_sim_info = None
        self._selectable_sims.clear_selectable_sims()

    def register_active_sim_changed(self, callback):
        if callback not in self._active_sim_changed:
            self._active_sim_changed.append(callback)

    def unregister_active_sim_changed(self, callback):
        if callback in self._active_sim_changed:
            self._active_sim_changed.remove(callback)

    def on_sim_added_to_skewer(self, sim_info, send_relationship_update=True):
        if send_relationship_update:
            sim_info.relationship_tracker.send_relationship_info()
        sim_info.relationship_tracker.enable_selectable_sim_track_decay()
        sim_info.on_sim_added_to_skewer()
        sim_info.commodity_tracker.send_commodity_progress_update()
        sim_info.career_tracker.on_sim_added_to_skewer()
        sim_info.send_whim_bucks_update(SetWhimBucks.LOAD)
        sim = sim_info.get_sim_instance(allow_hidden_flags=ALL_HIDDEN_REASONS)
        if sim is not None:
            sim.ui_manager.refresh_ui_data()
            services.autonomy_service().logging_sims.add(sim)
            sim.family_funds.empty_sim_personal_funds(sim)
            sim_info.aspiration_tracker.force_send_data_update()
            sim_info.aspiration_tracker.initialize_aspiration()
            sim_info.aspiration_tracker.set_update_alarm()
            sim_info.career_tracker.activate_career_aspirations()

    def on_sim_removed_from_skewer(self, sim_info, update_relationship_tracker=True):
        if update_relationship_tracker:
            if sim_info.is_child and sim_info.is_dead:
                sim_info.relationship_tracker.destroy_all_relationships()
            else:
                sim_info.relationship_tracker.enable_selectable_sim_track_decay(False)
        sim_info.aspiration_tracker.clear_update_alarm()
        sim = sim_info.get_sim_instance()
        if sim is not None:
            autonomy_service = services.autonomy_service()
            if autonomy_service is not None:
                autonomy_service.logging_sims.discard(sim)

    def clean_and_send_remaining_relationship_info(self):
        for sim_info in self.selectable_sims:
            sim_info.relationship_tracker.clean_and_send_remaining_relationship_info()

    def cancel_live_drag_on_objects(self):
        for obj in self._live_drag_objects:
            obj.live_drag_component.cancel_live_dragging()
        self._live_drag_objects = []

    def _get_stack_items_from_drag_object(self, drag_object, remove=False, is_stack=False):
        if drag_object.inventoryitem_component is None:
            return (False, None)
        previous_inventory = drag_object.inventoryitem_component.get_inventory()
        if previous_inventory is None:
            return (False, None)
        stack_id = drag_object.inventoryitem_component.get_stack_id()
        if remove:
            success = previous_inventory.try_remove_object_by_id(drag_object.id, force_remove_stack=is_stack)
        else:
            success = True
        stack_items = previous_inventory.get_stack_items(stack_id)
        return (success, stack_items)

    def remove_drag_object_and_get_next_item(self, drag_object):
        next_object_id = None
        (success, stack_items) = self._get_stack_items_from_drag_object(drag_object, remove=True)
        if success and stack_items:
            next_object_id = stack_items[0].id
        return (success, next_object_id)

    def get_live_drag_object_value(self, drag_object, is_stack=False):
        (_, stack_items) = self._get_stack_items_from_drag_object(drag_object, remove=False, is_stack=is_stack)
        value = 0
        if is_stack and stack_items:
            for item in stack_items:
                value += item.current_value*item.stack_count()
        else:
            value = drag_object.current_value
        return value

    def start_live_drag(self, live_drag_object, start_system, is_stack):
        self._live_drag_start_system = start_system
        success = True
        if is_stack:
            inventoryitem_component = live_drag_object.inventoryitem_component
            stack_id = inventoryitem_component.get_stack_id()
            current_inventory = inventoryitem_component.get_inventory()
            stack_items = current_inventory.get_stack_items(stack_id)
        else:
            stack_items = [live_drag_object]
        for item in stack_items:
            live_drag_component = live_drag_object.live_drag_component
            live_drag_component = item.live_drag_component
            if live_drag_component is None:
                logger_live_drag.error('Live Drag Start called on an object with no Live Drag Component. Object: {}'.format(item))
                self.send_live_drag_cancel(live_drag_object.id)
                return
            if item.in_use and not item.in_use_by(self) or not live_drag_component.can_live_drag:
                logger_live_drag.warn('Live Drag Start called on an object that is in use. Object: {}'.format(item))
                self.send_live_drag_cancel(item.id)
                return
            success = live_drag_component.start_live_dragging(self, start_system)
            if not success:
                break
            self._live_drag_objects.append(item)
        if not success:
            self.cancel_live_drag_on_objects()
            self.send_live_drag_cancel(live_drag_object.id, LiveDragLocation.INVALID)
        self._live_drag_is_stack = is_stack
        if gsi_handlers.live_drag_handlers.live_drag_archiver.enabled:
            gsi_handlers.live_drag_handlers.archive_live_drag('Start', 'Operation', LiveDragLocation.GAMEPLAY_SCRIPT, start_system, live_drag_object_id=live_drag_object.id)
        if live_drag_object.live_drag_component.active_household_has_sell_permission:
            sell_value = self.get_live_drag_object_value(live_drag_object, self._live_drag_is_stack) if live_drag_object.definition.get_is_deletable() else -1
        else:
            sell_value = -1
        (valid_drop_object_ids, valid_stack_id) = live_drag_component.get_valid_drop_object_ids()
        op = distributor.ops.LiveDragStart(live_drag_object.id, start_system, valid_drop_object_ids, valid_stack_id, sell_value)
        distributor_system = Distributor.instance()
        distributor_system.add_op_with_no_owner(op)

    def end_live_drag(self, source_object, target_object=None, end_system=LiveDragLocation.INVALID, location=None):
        live_drag_component = source_object.live_drag_component
        if live_drag_component is None:
            logger_live_drag.error('Live Drag End called on an object with no Live Drag Component. Object: {}'.format(source_object))
            self.send_live_drag_cancel(source_object.id, end_system)
            return
        if source_object not in self._live_drag_objects:
            logger_live_drag.warn('Live Drag End called on an object not being Live Dragged. Object: {}'.format(source_object))
            self.send_live_drag_cancel(source_object.id, end_system)
            return
        source_object_id = source_object.id
        self.cancel_live_drag_on_objects()
        next_object_id = None
        success = False
        if target_object is not None:
            live_drag_target_component = target_object.live_drag_target_component
            if live_drag_target_component is not None:
                (success, next_object_id) = live_drag_target_component.drop_live_drag_object(source_object, self._live_drag_is_stack)
            else:
                logger_live_drag.error('Live Drag Target Component missing on object: {} is now required on all drop targets.'.format(target_object))
                success = False
        else:
            success = True
            if location is not None:
                source_object.set_location(location)
            inventory_item = source_object.inventoryitem_component
            if inventory_item is not None and inventory_item.is_in_inventory():
                (success, next_object_id) = self.remove_drag_object_and_get_next_item(source_object)
        if success:
            if gsi_handlers.live_drag_handlers.live_drag_archiver.enabled:
                gsi_handlers.live_drag_handlers.archive_live_drag('End', 'Operation', LiveDragLocation.GAMEPLAY_SCRIPT, end_system, live_drag_object_id=source_object_id, live_drag_target=target_object)
            if not self._live_drag_is_stack:
                next_object_id = None
            op = distributor.ops.LiveDragEnd(source_object_id, self._live_drag_start_system, end_system, next_object_id)
            distributor_system = Distributor.instance()
            distributor_system.add_op_with_no_owner(op)
            self._live_drag_objects = []
            self._live_drag_start_system = LiveDragLocation.INVALID
            self._live_drag_is_stack = False
        else:
            self.send_live_drag_cancel(source_object_id, end_system)

    def cancel_live_drag(self, live_drag_object, end_system=LiveDragLocation.INVALID):
        live_drag_component = live_drag_object.live_drag_component
        if live_drag_component is None:
            logger_live_drag.warn('Live Drag Cancel called on an object with no Live Drag Component. Object: {}'.format(live_drag_object))
            self.send_live_drag_cancel(live_drag_object.id)
            return
        if live_drag_component.live_drag_state == LiveDragState.NOT_LIVE_DRAGGING:
            logger_live_drag.warn('Live Drag Cancel called on an object not being Live Dragged. Object: {}'.format(live_drag_object))
        else:
            self.cancel_live_drag_on_objects()
        self.send_live_drag_cancel(live_drag_object.id, end_system)

    def sell_live_drag_object(self, live_drag_object, end_system=LiveDragLocation.INVALID):
        live_drag_component = live_drag_object.live_drag_component
        if live_drag_component is None or not live_drag_object.definition.get_is_deletable():
            logger_live_drag.error("Live Drag Sell called on object with no Live Drag Component or can't be deleted. Object: {}".format(live_drag_object))
            self.send_live_drag_cancel(live_drag_object.id, end_system)
            return

        def sell_response(dialog):
            if not dialog.accepted:
                return
            value = int(self.get_live_drag_object_value(live_drag_object, self._live_drag_is_stack))
            live_drag_component.cancel_live_dragging(should_reset=False)
            object_tags = set()
            if self._live_drag_is_stack:
                (_, stack_items) = self._get_stack_items_from_drag_object(live_drag_object, remove=True, is_stack=True)
                for item in stack_items:
                    item.current_value = 0
                    item.set_stack_count(0)
                    object_tags.update(item.get_tags())
                    item.destroy(source=item, cause='Selling stack of live drag objects.')
            else:
                object_tags.update(live_drag_object.get_tags())
                if live_drag_object.is_in_inventory():
                    self.remove_drag_object_and_get_next_item(live_drag_object)
                else:
                    live_drag_object.remove_from_client()
            object_tags = frozenset(object_tags)
            live_drag_object.current_value = 0
            live_drag_object.destroy(source=live_drag_object, cause='Selling live drag object.')
            services.active_household().funds.add(value, Consts_pb2.TELEMETRY_OBJECT_SELL, self.active_sim, tags=object_tags)
            self._live_drag_objects = []
            self._live_drag_start_system = LiveDragLocation.INVALID
            self._live_drag_is_stack = False
            self._live_drag_sell_dialog_active = False

        if self._live_drag_is_stack:
            dialog = LiveDragTuning.LIVE_DRAG_SELL_STACK_DIALOG(owner=live_drag_object)
        else:
            dialog = LiveDragTuning.LIVE_DRAG_SELL_DIALOG(owner=live_drag_object)
        dialog.show_dialog(on_response=sell_response)
        self._live_drag_sell_dialog_active = True

    def send_live_drag_cancel(self, live_drag_object_id, live_drag_end_system=LiveDragLocation.INVALID):
        if gsi_handlers.live_drag_handlers.live_drag_archiver.enabled:
            gsi_handlers.live_drag_handlers.archive_live_drag('Cancel', 'Operation', LiveDragLocation.GAMEPLAY_SCRIPT, live_drag_end_system, live_drag_object_id=live_drag_object_id)
        op = distributor.ops.LiveDragCancel(live_drag_object_id, self._live_drag_start_system, live_drag_end_system)
        distributor_system = Distributor.instance()
        distributor_system.add_op_with_no_owner(op)
        if not self._live_drag_sell_dialog_active:
            self._live_drag_objects = []
            self._live_drag_start_system = LiveDragLocation.INVALID
            self._live_drag_is_stack = False

    def on_add(self):
        if self._account is not None:
            self._account.register_client(self)
        for sim_info in self._selectable_sims:
            self.on_sim_added_to_skewer(sim_info)
        distributor = Distributor.instance()
        distributor.add_object(self)
        distributor.add_client(self)
        self.send_selectable_sims_update()
        self.selectable_sims.add_watcher(self, self.send_selectable_sims_update)

    def on_remove(self):
        if self.active_sim is not None:
            self._set_active_sim_without_field_distribution(None)
        if self._account is not None:
            self._account.unregister_client(self)
        for sim_info in self._selectable_sims:
            self.on_sim_removed_from_skewer(sim_info, update_relationship_tracker=False)
        self.selectable_sims.remove_watcher(self)
        distributor = Distributor.instance()
        distributor.remove_client(self)
        self._selectable_sims = None
        self.active = False

    def get_objects_in_view_gen(self):
        for manager in services.client_object_managers():
            for obj in manager.get_all():
                yield obj

    def notify_active_sim_changed(self, old_sim, new_sim):
        self._active_sim_changed(old_sim, new_sim)

    def _get_selector_visual_type(self, sim_info):
        if sim_info.is_baby:
            return (Sims_pb2.SimPB.BABY, None)
        for career in sim_info.career_tracker.careers.values():
            if career.currently_at_work:
                return (Sims_pb2.SimPB.AT_WORK, career.career_category)
            while career.is_late:
                return (Sims_pb2.SimPB.LATE_FOR_WORK, career.career_category)
        sim = sim_info.get_sim_instance(allow_hidden_flags=ALL_HIDDEN_REASONS)
        if sim is not None and sim.has_hidden_flags(HiddenReasonFlag.RABBIT_HOLE):
            return (Sims_pb2.SimPB.OTHER, None)
        return (Sims_pb2.SimPB.NORMAL, None)

    def send_selectable_sims_update(self):
        msg = Sims_pb2.UpdateSelectableSims()
        for sim_info in self._selectable_sims:
            with ProtocolBufferRollback(msg.sims) as new_sim:
                new_sim.id = sim_info.sim_id
                new_sim.at_work = sim_info.career_tracker.currently_at_work
                new_sim.is_selectable = sim_info.is_enabled_in_skewer
                (selector_visual_type, career_category) = self._get_selector_visual_type(sim_info)
                new_sim.selector_visual_type = selector_visual_type
                if career_category is not None:
                    new_sim.career_category = career_category
                while not sim_info.is_instanced(allow_hidden_flags=ALL_HIDDEN_REASONS):
                    new_sim.instance_info.zone_id = sim_info.zone_id
                    new_sim.instance_info.world_id = sim_info.world_id
                    new_sim.firstname = sim_info.first_name
                    new_sim.lastname = sim_info.last_name
                    zone_data_proto = services.get_persistence_service().get_zone_proto_buff(sim_info.zone_id)
                    while zone_data_proto is not None:
                        new_sim.instance_info.zone_name = zone_data_proto.name
        distributor = Distributor.instance()
        distributor.add_op_with_no_owner(GenericProtocolBufferOp(Operation.SELECTABLE_SIMS_UPDATE, msg))

    @property
    def is_sim(self):
        return False
Esempio n. 43
0
class BuffComponent(objects.components.Component, component_name=objects.components.types.BUFF_COMPONENT):
    __qualname__ = 'BuffComponent'
    DEFAULT_MOOD = TunableReference(services.mood_manager(), description='The default initial mood.')
    UPDATE_INTENSITY_BUFFER = TunableRange(description="\n        A buffer that prevents a mood from becoming active unless its intensity\n        is greater than the current active mood's intensity plus this amount.\n        \n        For example, if this tunable is 1, and the Sim is in a Flirty mood with\n        intensity 2, then a different mood would become the active mood only if\n        its intensity is 3+.\n        \n        If the predominant mood has an intensity that is less than the active\n        mood's intensity, that mood will become the active mood.\n        ", tunable_type=int, default=1, minimum=0)
    EXCLUSIVE_SET = TunableList(description='\n        A list of buff groups to determine which buffs are exclusive from each\n        other within the same group.  A buff cannot exist in more than one exclusive group.\n        \n        The following rule of exclusivity for a group:\n        1. Higher weight will always be added and remove any lower weight buffs\n        2. Lower weight buff will not be added if a higher weight already exist in component\n        3. Same weight buff will always be added and remove any buff with same weight.\n        \n        Example: Group 1:\n                    Buff1 with weight of 5 \n                    Buff2 with weight of 1\n                    Buff3 with weight of 1\n                 Group 2:\n                    Buff4 with weight of 6\n        \n        If sim has Buff1, trying to add Buff2 or Buff3 will not be added.\n        If sim has Buff2, trying to add Buff3 will remove Buff2 and add Buff3\n        If sim has Buff2, trying to add Buff1 will remove Buff 2 and add Buff3\n        If sim has Buff4, trying to add Buff1, Buff2, or Buff3 will be added and Buff4 will stay \n                          on component \n        ', tunable=TunableList(tunable=TunableTuple(buff_type=TunableReference(description='\n                    Buff in exclusive group\n                    ', manager=services.get_instance_manager(sims4.resources.Types.BUFF)), weight=Tunable(description='\n                    weight to determine if this buff should be added and\n                    remove other buffs in the exclusive group or not added at all.\n                    \n                    Example: Buff1 with weight of 5 \n                             Buff2 with weight of 1\n                             Buff3 with weight of 1\n                    \n                    If sim has Buff1, trying to add Buff2 or Buff3 will not be added.\n                    If sim has Buff2, trying to add Buff3 will remove Buff2 and add Buff3\n                    if sim has Buff2, trying to add Buff1 will remove Buff 2 and add Buff3\n                    ', tunable_type=int, default=1))))

    def __init__(self, owner):
        super().__init__(owner)
        self._active_buffs = {}
        self._get_next_handle_id = UniqueIdGenerator()
        self._success_chance_modification = 0
        self._active_mood = self.DEFAULT_MOOD
        self._active_mood_intensity = 0
        self._active_mood_buff_handle = None
        self.on_mood_changed = CallableList()
        self.on_mood_changed.append(self._publish_mood_update)
        self.on_mood_changed.append(self._send_mood_changed_event)
        self.load_in_progress = False
        self.on_buff_added = CallableList()
        self.on_buff_removed = CallableList()
        self.buff_update_alarms = {}
        if self._active_mood is None:
            logger.error('No default mood tuned in buff_component.py')
        elif self._active_mood.buffs:
            initial_buff_ref = self._active_mood.buffs[0]
            if initial_buff_ref and initial_buff_ref.buff_type:
                self._active_mood_buff_handle = self.add_buff(initial_buff_ref.buff_type)

    def __iter__(self):
        return self._active_buffs.values().__iter__()

    def __len__(self):
        return len(self._active_buffs)

    def on_sim_ready_to_simulate(self):
        for buff in self:
            buff.on_sim_ready_to_simulate()
        self._publish_mood_update()

    def on_sim_removed(self, *args, **kwargs):
        for buff in self:
            buff.on_sim_removed(*args, **kwargs)

    def clean_up(self):
        for (buff_type, buff_entry) in tuple(self._active_buffs.items()):
            self.remove_auto_update(buff_type)
            buff_entry.clean_up()
        self._active_buffs.clear()
        self.on_mood_changed.clear()
        self.on_buff_added.clear()
        self.on_buff_removed.clear()

    @objects.components.componentmethod
    def add_buff_from_op(self, buff_type, buff_reason=None):
        (can_add, _) = self._can_add_buff_type(buff_type)
        if not can_add:
            return False
        buff_commodity = buff_type.commodity
        if buff_commodity is not None:
            if not buff_type.refresh_on_add and self.has_buff(buff_type):
                return False
            tracker = self.owner.get_tracker(buff_commodity)
            if buff_commodity.convergence_value == buff_commodity.max_value:
                tracker.set_min(buff_commodity)
            else:
                tracker.set_max(buff_commodity)
            self.set_buff_reason(buff_type, buff_reason, use_replacement=True)
        else:
            self.add_buff(buff_type, buff_reason=buff_reason)
        return True

    @objects.components.componentmethod
    def add_buff(self, buff_type, buff_reason=None, update_mood=True, commodity_guid=None, replacing_buff=None, timeout_string=None, transition_into_buff_id=0, change_rate=None, immediate=False):
        replacement_buff_type = self._get_replacement_buff_type(buff_type)
        if replacement_buff_type is not None:
            return self.owner.add_buff(replacement_buff_type, buff_reason=buff_reason, update_mood=update_mood, commodity_guid=commodity_guid, replacing_buff=buff_type, timeout_string=timeout_string, transition_into_buff_id=transition_into_buff_id, change_rate=change_rate, immediate=immediate)
        (can_add, conflicting_buff_type) = self._can_add_buff_type(buff_type)
        if not can_add:
            return
        buff = self._active_buffs.get(buff_type)
        if buff is None:
            buff = buff_type(self.owner, commodity_guid, replacing_buff, transition_into_buff_id)
            self._active_buffs[buff_type] = buff
            buff.on_add(self.load_in_progress)
            self._update_chance_modifier()
            if update_mood:
                self._update_current_mood()
            if self.owner.household is not None:
                services.get_event_manager().process_event(test_events.TestEvent.BuffBeganEvent, sim_info=self.owner, sim_id=self.owner.sim_id, buff=buff_type)
                self.register_auto_update(self.owner, buff_type)
            self.on_buff_added(buff_type)
        handle_id = self._get_next_handle_id()
        buff.add_handle(handle_id, buff_reason=buff_reason)
        self.send_buff_update_msg(buff, True, change_rate=change_rate, immediate=immediate)
        if conflicting_buff_type is not None:
            self.remove_buff_by_type(conflicting_buff_type)
        return handle_id

    def _get_replacement_buff_type(self, buff_type):
        if buff_type.trait_replacement_buffs is not None:
            trait_tracker = self.owner.trait_tracker
            for (trait, replacement_buff_type) in buff_type.trait_replacement_buffs.items():
                while trait_tracker.has_trait(trait):
                    return replacement_buff_type

    def register_auto_update(self, sim_info_in, buff_type_in):
        if buff_type_in in self.buff_update_alarms:
            self.remove_auto_update(buff_type_in)
        if sim_info_in.is_selectable and buff_type_in.visible:
            self.buff_update_alarms[buff_type_in] = alarms.add_alarm(self, create_time_span(minutes=15), lambda _, sim_info=sim_info_in, buff_type=buff_type_in: services.get_event_manager().process_event(test_events.TestEvent.BuffUpdateEvent, sim_info=sim_info, sim_id=sim_info.sim_id, buff=buff_type), True)

    def remove_auto_update(self, buff_type):
        if buff_type in self.buff_update_alarms:
            alarms.cancel_alarm(self.buff_update_alarms[buff_type])
            del self.buff_update_alarms[buff_type]

    @objects.components.componentmethod
    def remove_buff(self, handle_id, update_mood=True, immediate=False, on_destroy=False):
        for (buff_type, buff_entry) in self._active_buffs.items():
            while handle_id in buff_entry.handle_ids:
                should_remove = buff_entry.remove_handle(handle_id)
                if should_remove:
                    del self._active_buffs[buff_type]
                    buff_entry.on_remove(not self.load_in_progress and not on_destroy)
                    if not on_destroy:
                        if update_mood:
                            self._update_current_mood()
                        self._update_chance_modifier()
                        self.send_buff_update_msg(buff_entry, False, immediate=immediate)
                        services.get_event_manager().process_event(test_events.TestEvent.BuffEndedEvent, sim_info=self.owner, sim_id=self.owner.sim_id, buff=buff_type)
                    if buff_type in self.buff_update_alarms:
                        self.remove_auto_update(buff_type)
                    self.on_buff_removed(buff_type)
                break

    @objects.components.componentmethod
    def get_buff_type(self, handle_id):
        for (buff_type, buff_entry) in self._active_buffs.items():
            while handle_id in buff_entry.handle_ids:
                return buff_type

    @objects.components.componentmethod
    def has_buff(self, buff_type):
        return buff_type in self._active_buffs

    @objects.components.componentmethod
    def get_active_buff_types(self):
        return self._active_buffs.keys()

    @objects.components.componentmethod
    def get_buff_reason(self, handle_id):
        for buff_entry in self._active_buffs.values():
            while handle_id in buff_entry.handle_ids:
                return buff_entry.buff_reason

    @objects.components.componentmethod
    def debug_add_buff_by_type(self, buff_type):
        (can_add, conflicting_buff_type) = self._can_add_buff_type(buff_type)
        if not can_add:
            return False
        if buff_type.commodity is not None:
            tracker = self.owner.get_tracker(buff_type.commodity)
            state_index = buff_type.commodity.get_state_index_matches_buff_type(buff_type)
            if state_index is not None:
                index = state_index + 1
                if index < len(buff_type.commodity.commodity_states):
                    commodity_to_value = buff_type.commodity.commodity_states[index].value - 1
                else:
                    commodity_to_value = buff_type.commodity.max_value
                tracker.set_value(buff_type.commodity, commodity_to_value)
            else:
                logger.error('commodity ({}) has no states with buff ({}), Buff will not be added.', buff_type.commodity, buff_type)
                return False
        else:
            self.add_buff(buff_type)
        if conflicting_buff_type is not None:
            self.remove_buff_by_type(conflicting_buff_type)
        return True

    @objects.components.componentmethod
    def remove_buff_by_type(self, buff_type, on_destroy=False):
        buff_entry = self._active_buffs.get(buff_type)
        self.remove_buff_entry(buff_entry, on_destroy=on_destroy)

    @objects.components.componentmethod
    def remove_buff_entry(self, buff_entry, on_destroy=False):
        if buff_entry is not None:
            if buff_entry.commodity is not None:
                tracker = self.owner.get_tracker(buff_entry.commodity)
                commodity_inst = tracker.get_statistic(buff_entry.commodity)
                if commodity_inst is not None and commodity_inst.core:
                    if not on_destroy:
                        logger.callstack('Attempting to explicitly remove the buff {}, which is given by a core commodity.                                           This would result in the removal of a core commodity and will be ignored.', buff_entry, owner='tastle', level=sims4.log.LEVEL_ERROR)
                    return
                tracker.remove_statistic(buff_entry.commodity, on_destroy=on_destroy)
            elif buff_entry.buff_type in self._active_buffs:
                buff_entry.on_remove(on_destroy)
                del self._active_buffs[buff_entry.buff_type]
                if not on_destroy:
                    self._update_chance_modifier()
                    self._update_current_mood()
                    self.send_buff_update_msg(buff_entry, False)
                    services.get_event_manager().process_event(test_events.TestEvent.BuffEndedEvent, sim_info=self.owner, buff=type(buff_entry), sim_id=self.owner.id)

    @objects.components.componentmethod
    def set_buff_reason(self, buff_type, buff_reason, use_replacement=False):
        if use_replacement:
            replacement_buff_type = self._get_replacement_buff_type(buff_type)
            if replacement_buff_type is not None:
                buff_type = replacement_buff_type
        buff_entry = self._active_buffs.get(buff_type)
        if buff_entry is not None and buff_reason is not None:
            buff_entry.buff_reason = buff_reason
            self.send_buff_update_msg(buff_entry, True)

    @objects.components.componentmethod
    def buff_commodity_changed(self, handle_id, change_rate=None):
        for (_, buff_entry) in self._active_buffs.items():
            while handle_id in buff_entry.handle_ids:
                if buff_entry.show_timeout:
                    self.send_buff_update_msg(buff_entry, True, change_rate=change_rate)
                break

    @objects.components.componentmethod
    def get_success_chance_modifier(self):
        return self._success_chance_modification

    @objects.components.componentmethod
    def get_actor_scoring_modifier(self, affordance):
        total = 0
        for buff_entry in self._active_buffs.values():
            total += buff_entry.effect_modification.get_affordance_scoring_modifier(affordance)
        return total

    @objects.components.componentmethod
    def get_actor_success_modifier(self, affordance):
        total = 0
        for buff_entry in self._active_buffs.values():
            total += buff_entry.effect_modification.get_affordance_success_modifier(affordance)
        return total

    @objects.components.componentmethod
    def get_mood(self):
        return self._active_mood

    @objects.components.componentmethod
    def get_mood_animation_param_name(self):
        param_name = self._active_mood.asm_param_name
        if param_name is not None:
            return param_name
        (mood, _, _) = self._get_largest_mood(predicate=lambda mood: return True if mood.asm_param_name else False)
        return mood.asm_param_name

    @objects.components.componentmethod
    def get_mood_intensity(self):
        return self._active_mood_intensity

    @objects.components.componentmethod
    def get_effective_skill_level(self, skill):
        if skill.stat_type == skill:
            skill = self.owner.get_stat_instance(skill)
            if skill is None:
                return 0
        modifier = 0
        for buff_entry in self._active_buffs.values():
            modifier += buff_entry.effect_modification.get_effective_skill_modifier(skill)
        return skill.get_user_value() + modifier

    @objects.components.componentmethod
    def effective_skill_modified_buff_gen(self, skill):
        if skill.stat_type == skill:
            skill = self.owner.get_stat_instance(skill)
        for buff_entry in self._active_buffs.values():
            modifier = buff_entry.effect_modification.get_effective_skill_modifier(skill)
            while modifier != 0:
                yield (buff_entry, modifier)

    @objects.components.componentmethod
    def is_appropriate(self, tags):
        final_appropriateness = Appropriateness.DONT_CARE
        for buff in self._active_buffs:
            appropriateness = buff.get_appropriateness(tags)
            while appropriateness > final_appropriateness:
                final_appropriateness = appropriateness
        if final_appropriateness == Appropriateness.NOT_ALLOWED:
            return False
        return True

    def get_additional_create_ops_gen(self):
        yield GenericProtocolBufferOp(Operation.SIM_MOOD_UPDATE, self._create_mood_update_msg())
        for buff in self:
            while buff.visible:
                yield GenericProtocolBufferOp(Operation.SIM_BUFF_UPDATE, self._create_buff_update_msg(buff, True))

    def _publish_mood_update(self):
        if self.owner.valid_for_distribution and self.owner.visible_to_client == True:
            Distributor.instance().add_op(self.owner, GenericProtocolBufferOp(Operation.SIM_MOOD_UPDATE, self._create_mood_update_msg()))

    def _send_mood_changed_event(self):
        if not self.owner.is_npc:
            self.owner.whim_tracker.refresh_emotion_whim()
        services.get_event_manager().process_event(test_events.TestEvent.MoodChange, sim_info=self.owner)

    def _create_mood_update_msg(self):
        mood_msg = Commodities_pb2.MoodUpdate()
        mood_msg.sim_id = self.owner.id
        mood_msg.mood_key = self._active_mood.guid64
        mood_msg.mood_intensity = self._active_mood_intensity
        return mood_msg

    def _create_buff_update_msg(self, buff, equipped, change_rate=None):
        buff_msg = Sims_pb2.BuffUpdate()
        buff_msg.buff_id = buff.guid64
        buff_msg.sim_id = self.owner.id
        buff_msg.equipped = equipped
        if buff.buff_reason is not None:
            buff_msg.reason = buff.buff_reason
        if equipped and buff.show_timeout:
            (timeout, rate_multiplier) = buff.get_timeout_time()
            buff_msg.timeout = timeout
            buff_msg.rate_multiplier = rate_multiplier
            if change_rate is not None:
                if change_rate == 0:
                    progress_arrow = Sims_pb2.BUFF_PROGRESS_NONE
                elif change_rate > 0:
                    progress_arrow = Sims_pb2.BUFF_PROGRESS_UP if not buff.flip_arrow_for_progress_update else Sims_pb2.BUFF_PROGRESS_DOWN
                else:
                    progress_arrow = Sims_pb2.BUFF_PROGRESS_DOWN if not buff.flip_arrow_for_progress_update else Sims_pb2.BUFF_PROGRESS_UP
                buff_msg.buff_progress = progress_arrow
        buff_msg.is_mood_buff = buff.is_mood_buff
        buff_msg.commodity_guid = buff.commodity_guid or 0
        if buff.mood_override is not None:
            buff_msg.mood_type_override = buff.mood_override.guid64
        buff_msg.transition_into_buff_id = buff.transition_into_buff_id
        return buff_msg

    def send_buff_update_msg(self, buff, equipped, change_rate=None, immediate=False):
        if not buff.visible:
            return
        if self.owner.valid_for_distribution and self.owner.is_sim and self.owner.is_selectable:
            buff_msg = self._create_buff_update_msg(buff, equipped, change_rate=change_rate)
            if gsi_handlers.buff_handlers.sim_buff_log_archiver.enabled:
                gsi_handlers.buff_handlers.archive_buff_message(buff_msg, equipped, change_rate)
            Distributor.instance().add_op(self.owner, GenericProtocolBufferOp(Operation.SIM_BUFF_UPDATE, buff_msg))

    def _can_add_buff_type(self, buff_type):
        if not buff_type.can_add(self.owner):
            return (False, None)
        mood = buff_type.mood_type
        if mood is not None and mood.excluding_traits is not None and self.owner.trait_tracker.has_any_trait(mood.excluding_traits):
            return (False, None)
        if buff_type.exclusive_index is None:
            return (True, None)
        for conflicting_buff_type in self._active_buffs:
            while conflicting_buff_type.exclusive_index == buff_type.exclusive_index:
                if buff_type.exclusive_weight < conflicting_buff_type.exclusive_weight:
                    return (False, None)
                return (True, conflicting_buff_type)
        return (True, None)

    def _update_chance_modifier(self):
        positive_success_buff_delta = 0
        negative_success_buff_delta = 1
        for buff_entry in self._active_buffs.values():
            if buff_entry.success_modifier > 0:
                positive_success_buff_delta += buff_entry.get_success_modifier
            else:
                negative_success_buff_delta *= 1 + buff_entry.get_success_modifier
        self._success_chance_modification = positive_success_buff_delta - (1 - negative_success_buff_delta)

    def _get_largest_mood(self, predicate=None, buffs_to_ignore=()):
        weights = {}
        polarity_to_changeable_buffs = collections.defaultdict(list)
        polarity_to_largest_mood_and_weight = {}
        for buff_entry in self._active_buffs.values():
            current_mood = buff_entry.mood_type
            current_weight = buff_entry.mood_weight
            while not current_mood is None:
                if current_weight == 0:
                    pass
                if not (predicate is not None and predicate(current_mood)):
                    pass
                if buff_entry in buffs_to_ignore:
                    pass
                current_polarity = current_mood.buff_polarity
                if buff_entry.is_changeable:
                    polarity_to_changeable_buffs[current_polarity].append(buff_entry)
                total_current_weight = weights.get(current_mood, 0)
                total_current_weight += current_weight
                weights[current_mood] = total_current_weight
                (largest_mood, largest_weight) = polarity_to_largest_mood_and_weight.get(current_polarity, (None, None))
                if largest_mood is None:
                    polarity_to_largest_mood_and_weight[current_polarity] = (current_mood, total_current_weight)
                else:
                    while total_current_weight > largest_weight:
                        polarity_to_largest_mood_and_weight[current_polarity] = (current_mood, total_current_weight)
        all_changeable_buffs = []
        for (buff_polarity, changeable_buffs) in polarity_to_changeable_buffs.items():
            (largest_mood, largest_weight) = polarity_to_largest_mood_and_weight.get(buff_polarity, (None, None))
            if largest_mood is not None:
                for buff_entry in changeable_buffs:
                    if buff_entry.mood_override is not largest_mood:
                        all_changeable_buffs.append((buff_entry, largest_mood))
                    largest_weight += buff_entry.mood_weight
                polarity_to_largest_mood_and_weight[buff_polarity] = (largest_mood, largest_weight)
            else:
                weights = {}
                largest_weight = 0
                for buff_entry in changeable_buffs:
                    if buff_entry.mood_override is not None:
                        all_changeable_buffs.append((buff_entry, None))
                    current_mood = buff_entry.mood_type
                    current_weight = buff_entry.mood_weight
                    total_current_weight = weights.get(current_mood, 0)
                    total_current_weight += current_weight
                    weights[current_mood] = total_current_weight
                    while total_current_weight > largest_weight:
                        largest_weight = total_current_weight
                        largest_mood = current_mood
                while largest_mood is not None and largest_weight != 0:
                    polarity_to_largest_mood_and_weight[buff_polarity] = (largest_mood, largest_weight)
        largest_weight = 0
        largest_mood = self.DEFAULT_MOOD
        active_mood = self._active_mood
        if polarity_to_largest_mood_and_weight:
            (mood, weight) = max(polarity_to_largest_mood_and_weight.values(), key=operator.itemgetter(1))
            if weight > largest_weight or weight == largest_weight and mood is active_mood:
                largest_weight = weight
                largest_mood = mood
        return (largest_mood, largest_weight, all_changeable_buffs)

    def _update_current_mood(self):
        (largest_mood, largest_weight, changeable_buffs) = self._get_largest_mood()
        if largest_mood is not None:
            intensity = self._get_intensity_from_mood(largest_mood, largest_weight)
            if self._should_update_mood(largest_mood, intensity, changeable_buffs):
                if self._active_mood_buff_handle is not None:
                    active_mood_buff_handle = self._active_mood_buff_handle
                    self.remove_buff(active_mood_buff_handle, update_mood=False)
                    if active_mood_buff_handle == self._active_mood_buff_handle:
                        self._active_mood_buff_handle = None
                    else:
                        return
                self._active_mood = largest_mood
                self._active_mood_intensity = intensity
                if len(largest_mood.buffs) >= intensity:
                    tuned_buff = largest_mood.buffs[intensity]
                    if tuned_buff is not None and tuned_buff.buff_type is not None:
                        self._active_mood_buff_handle = self.add_buff(tuned_buff.buff_type, update_mood=False)
                if gsi_handlers.buff_handlers.sim_mood_log_archiver.enabled and self.owner.valid_for_distribution and self.owner.visible_to_client == True:
                    gsi_handlers.buff_handlers.archive_mood_message(self.owner.id, self._active_mood, self._active_mood_intensity, self._active_buffs, changeable_buffs)
                caches.clear_all_caches()
                self.on_mood_changed()
        for (changeable_buff, mood_override) in changeable_buffs:
            changeable_buff.mood_override = mood_override
            self.send_buff_update_msg(changeable_buff, True)

    def _get_intensity_from_mood(self, mood, weight):
        intensity = 0
        for threshold in mood.intensity_thresholds:
            if weight >= threshold:
                intensity += 1
            else:
                break
        return intensity

    def _should_update_mood(self, mood, intensity, changeable_buffs):
        active_mood = self._active_mood
        active_mood_intensity = self._active_mood_intensity
        if mood is active_mood:
            return intensity != active_mood_intensity
        total_weight = sum(buff_entry.mood_weight for buff_entry in self._active_buffs.values() if buff_entry.mood_type is active_mood)
        active_mood_intensity = self._get_intensity_from_mood(active_mood, total_weight)
        if changeable_buffs and not self._active_mood.is_changeable:
            buffs_to_ignore = [changeable_buff for (changeable_buff, _) in changeable_buffs]
            (largest_mood, largest_weight, _) = self._get_largest_mood(buffs_to_ignore=buffs_to_ignore)
            new_intensity = self._get_intensity_from_mood(largest_mood, largest_weight)
            if self._should_update_mood(largest_mood, new_intensity, None):
                active_mood = largest_mood
                active_mood_intensity = new_intensity
        if active_mood.is_changeable and mood.buff_polarity == active_mood.buff_polarity:
            return True
        if not intensity or intensity < active_mood_intensity:
            return True
        if intensity >= active_mood_intensity + self.UPDATE_INTENSITY_BUFFER:
            return True
        if mood is self.DEFAULT_MOOD or active_mood is self.DEFAULT_MOOD:
            return True
        return False
class InstanceManager:
    __qualname__ = 'InstanceManager'

    def __init__(self, path, type_enum, use_guid_for_ref=False):
        self._tuned_classes = {}
        self._callback_helper = {}
        self._verify_tunable_callback_helper = {}
        self._class_templates = []
        self.PATH = path
        self.TYPE = type_enum
        self._load_all_complete = False
        self._load_all_complete_callbacks = CallableList()
        self._use_guid_for_ref = use_guid_for_ref

    def add_on_load_complete(self, callback):
        if not self._load_all_complete:
            self._load_all_complete_callbacks.append(callback)
        else:
            callback(self)

    @property
    def all_instances_loaded(self):
        return self._load_all_complete

    def __str__(self):
        return 'InstanceManager_{}'.format(self.TYPE.name.lower())

    def get_changed_files(self):
        return sims4.core_services.file_change_manager().consume_set(self.TYPE)

    def reload_by_key(self, key):
        if not __debug__:
            raise RuntimeError('[manus] Reloading tuning is not supported for optimized python builds.')
        cls = self._tuned_classes.get(key)
        if cls is None:
            self.get(key)
            return
        try:
            sims4.tuning.serialization.restore_class_instance(cls)
            (tuning_callbacks, verify_callbacks) = sims4.tuning.serialization.load_from_xml(key, self.TYPE, cls, from_reload=True)
            if tuning_callbacks:
                for helper in tuning_callbacks:
                    helper.template.invoke_callback(cls, helper.name, helper.source, helper.value)
            if verify_callbacks:
                for helper in verify_callbacks:
                    helper.template.invoke_verify_tunable_callback(cls, helper.name, helper.source, helper.value)
            if hasattr(cls, TUNING_LOADED_CALLBACK):
                cls._tuning_loaded_callback()
            while hasattr(cls, VERIFY_TUNING_CALLBACK):
                cls._verify_tuning_callback()
        except:
            name = sims4.resources.get_name_from_key(key)
            logger.exception('Failed to reload tuning for {} (key:{}).', name, key, owner='manus')
            return
        return reload_dependencies_dict[key]

    @property
    def types(self):
        if not self.all_instances_loaded:
            logger.warn("Attempt to access instance types on '{}' before all instances are loaded", self)
        return self._tuned_classes

    def get_ordered_types(self, only_subclasses_of=object):

        def key(cls):
            result = tuple(x.__name__.lower() for x in reversed(cls.__mro__[:-1]))
            return (len(result), result)

        result = [c for c in self.types.values() if issubclass(c, only_subclasses_of)]
        result = sorted(result, key=key)
        return result

    @property
    def use_guid_for_ref(self):
        return self._use_guid_for_ref

    def register_class_template(self, template):
        self._class_templates.append(template)

    def register_tuned_class(self, instance, resource_key):
        if resource_key in self._tuned_classes:
            logger.info('Attempting to re-register class instance {} (Key:{}) with {}.', self._tuned_classes[resource_key], resource_key, self, owner='manus')
            return
        self._tuned_classes[resource_key] = instance
        instance.resource_key = resource_key
        if self.use_guid_for_ref:
            instance.guid64 = resource_key.instance

    def on_start(self):
        if sims4.core_services.SUPPORT_RELOADING_RESOURCES:
            file_change_manager = sims4.core_services.file_change_manager()
            if file_change_manager is not None:
                file_change_manager.create_set(self.TYPE, self.TYPE)

    def on_stop(self):
        if sims4.core_services.SUPPORT_RELOADING_RESOURCES:
            file_change_manager = sims4.core_services.file_change_manager()
            if file_change_manager is not None:
                file_change_manager.remove_set(self.TYPE)

    def create_class_instances(self):
        mtg = get_manager()
        res_id_list = mtg.get_all_res_ids(self.TYPE)
        logger.info('Creating {:4} tuning class instances managed by {}.', len(res_id_list), self, owner='manus')
        for (group_id, instance_id) in res_id_list:
            res_key = sims4.resources.Key(self.TYPE, instance_id, group_id)
            self._create_class_instance(res_key)

    def _create_class_instance(self, resource_key):
        cls = None
        try:
            registered_resource_key = sims4.resources.Key(self.TYPE, resource_key.instance)
            cls = sims4.tuning.serialization.create_class_instance(resource_key, self.TYPE)
            while cls is not None:
                self.register_tuned_class(cls, registered_resource_key)
        except Exception:
            if registered_resource_key in self._tuned_classes:
                del self._tuned_classes[registered_resource_key]
            logger.exception('An error occurred while attempting to create tuning instance: {}. Resource Key: {}.', cls, resource_key, owner='manus')

    def load_data_into_class_instances(self):
        logger.info('Loading {:4} tuning class instances managed by {}.', len(self._tuned_classes), self, owner='manus')
        for (key, cls) in self._tuned_classes.items():
            try:
                (tuning_callback_helpers, verify_tunable_callback_helpers) = sims4.tuning.serialization.load_from_xml(key, self.TYPE, cls)
                while tuning_callback_helpers:
                    self._callback_helper[cls] = tuning_callback_helpers
            except Exception:
                logger.exception('Exception while finalizing tuning for {}.', cls, owner='manus')

    def invoke_registered_callbacks_gen(self):
        logger.info('Invoking callbacks for {:4} tuning class instances managed by {}.', len(self._tuned_classes), self, owner='manus')
        invoke_verifications = False
        for cls in self._tuned_classes.values():
            self._invoke_tunable_callbacks(cls)
            if invoke_verifications:
                self._invoke_verify_tunable_callbacks(cls)
            try:
                if hasattr(cls, TUNING_LOADED_CALLBACK):
                    cls._tuning_loaded_callback()
                while invoke_verifications and hasattr(cls, VERIFY_TUNING_CALLBACK):
                    cls._verify_tuning_callback()
            except Exception:
                logger.exception('Exception in {}.{}.', cls, TUNING_LOADED_CALLBACK, owner='manus')
            yield False
        self._callback_helper = None
        self._verify_tunable_callback_helper = None
        self._load_all_complete = True
        self._load_all_complete_callbacks(self)

    def _invoke_tunable_callbacks(self, cls):
        tuning_callbacks = self._callback_helper.get(cls)
        if tuning_callbacks is None:
            return
        for helper in tuning_callbacks:
            try:
                helper.template.invoke_callback(cls, helper.name, helper.source, helper.value)
            except Exception:
                logger.exception('Exception in a tunable callback for variable {} in instance class {}.', helper.name, cls, owner='manus')

    def _invoke_verify_tunable_callbacks(self, cls):
        tuning_callbacks = self._verify_tunable_callback_helper.get(cls)
        if tuning_callbacks is None:
            return
        for helper in tuning_callbacks:
            try:
                helper.template.invoke_verify_tunable_callback(cls, helper.name, helper.source, helper.value)
            except Exception:
                logger.exception('Exception in a verify tunable callback for variable {} in instance class {}.', helper.name, cls, owner='manus')

    def get(self, name_or_id_or_key, pack_safe=False):
        key = sims4.resources.get_resource_key(name_or_id_or_key, self.TYPE)
        cls = self._tuned_classes.get(key)
        if cls is None:
            if not pack_safe:
                return
            raise UnavailablePackSafeResourceError
        return cls

    def _instantiate(self, target_type):
        return target_type()

    def export_descriptions(self, export_path, filter_fn=None):
        export_path = os.path.join(os.path.dirname(export_path), os.path.basename(self.PATH))
        export_path = os.path.join(export_path, 'Descriptions')
        to_export = {}
        for cls in sorted(self._class_templates, key=lambda cls: cls.__name__):
            cls_name = cls.__name__
            while filter_fn is None or filter_fn(cls):
                to_export[cls_name] = cls
        error_count = 0
        logger.info('TDESCs for {}: {}', self.TYPE.name, ', '.join([cls.__name__ for cls in to_export.values()]))
        for cls in to_export.values():
            result = sims4.tuning.serialization.export_class(cls, export_path, self.TYPE)
            while not result:
                error_count += 1
        return error_count

    def get_debug_statistics(self):
        result = []
        result.append(('TYPE', str(self.TYPE)))
        result.append(('PATH', str(self.PATH)))
        result.append(('UseGuidForReference', str(self._use_guid_for_ref)))
        result.append(('#TuningFiles', str(len(self._tuned_classes))))
        result.append(('#ClassTemplates', str(len(self._class_templates))))
        result.append(('LoadAllComplete', str(self._load_all_complete)))
        result.append(('#LoadAllCompelteCallbacks', str(len(self._load_all_complete_callbacks))))
        return result
class AwayActionTracker:
    __qualname__ = 'AwayActionTracker'

    def __init__(self, sim_info):
        self._sim_info = sim_info
        self._current_away_action = None
        self._on_away_action_started = CallableList()
        self._on_away_action_ended = CallableList()
        self.add_on_away_action_started_callback(self._resend_away_action)
        self.add_on_away_action_ended_callback(self._resend_away_action)

    @property
    def sim_info(self):
        return self._sim_info

    @property
    def current_away_action(self):
        return self._current_away_action

    def _resend_away_action(self, _):
        self.sim_info.resend_current_away_action()

    def is_sim_info_valid_to_run_away_actions(self):
        if not self._sim_info.is_selectable:
            return False
        if self._sim_info.is_dead:
            return False
        if self._sim_info.is_baby:
            return False
        return True

    def _run_current_away_action(self):
        self._current_away_action.run(self._away_action_exit_condition_callback)
        self._on_away_action_started(self._current_away_action)

    def _find_away_action_from_load(self):
        away_actions_manager = services.get_instance_manager(sims4.resources.Types.AWAY_ACTION)
        for away_action_cls in away_actions_manager.types.values():
            while away_action_cls.should_run_on_load(self.sim_info):
                return away_action_cls

    def start(self, on_travel_away=False):
        if not self.is_sim_info_valid_to_run_away_actions():
            logger.error('Attempting to start away action tracker on invalid sim info {}.', self._sim_info, owner='jjacobson')
            return
        if self._sim_info.is_instanced(allow_hidden_flags=ALL_HIDDEN_REASONS) and not on_travel_away:
            if self._current_away_action is not None:
                if not self._current_away_action.available_when_instanced:
                    self._current_away_action = None
                    return
                    return
            else:
                return
        if self._current_away_action is not None:
            self._run_current_away_action()
            return
        away_action_cls = self._find_away_action_from_load()
        if away_action_cls is not None:
            self.create_and_apply_away_action(away_action_cls)
            return
        self.reset_to_default_away_action(on_travel_away=on_travel_away)

    def stop(self):
        if self._current_away_action is not None:
            if self._current_away_action.is_running:
                self._current_away_action.stop()
                self._on_away_action_ended(self._current_away_action)
            self._current_away_action = None

    def clean_up(self):
        self.remove_on_away_action_started_callback(self._resend_away_action)
        self.remove_on_away_action_ended_callback(self._resend_away_action)
        self.stop()

    def refresh(self, on_travel_away=False):
        if not self.is_sim_info_valid_to_run_away_actions():
            return
        current_zone = services.current_zone()
        if not current_zone.is_zone_running and not current_zone.are_sims_hitting_their_marks:
            return
        if self._sim_info.zone_id == services.current_zone_id():
            self.stop()
            self._current_away_action = None
        else:
            self.start(on_travel_away=on_travel_away)

    def create_and_apply_away_action(self, away_action_cls, target=None):
        if not self.is_sim_info_valid_to_run_away_actions():
            logger.warn('Attempting to apply away action on invalid sim info {}.', self._sim_info, owner='jjacobson')
            return
        self.stop()
        self._current_away_action = away_action_cls(self, target=target)
        self._run_current_away_action()

    def _away_action_exit_condition_callback(self, _):
        self.reset_to_default_away_action()

    def reset_to_default_away_action(self, on_travel_away=False):
        default_away_action = self.sim_info.get_default_away_action(on_travel_away=on_travel_away)
        if default_away_action is None:
            self.stop()
            return
        self.create_and_apply_away_action(default_away_action)

    def save_away_action_info_to_proto(self, away_action_tracker_proto):
        if self._current_away_action is not None:
            away_action_tracker_proto.away_action.away_action_id = self._current_away_action.guid64
            target = self._current_away_action.target
            if target is not None:
                away_action_tracker_proto.away_action.target_sim_id = target.id

    def load_away_action_info_from_proto(self, away_action_tracker_proto):
        if away_action_tracker_proto.HasField('away_action'):
            away_action_cls = services.get_instance_manager(sims4.resources.Types.AWAY_ACTION).get(away_action_tracker_proto.away_action.away_action_id)
            if away_action_tracker_proto.away_action.HasField('target_sim_id'):
                target = services.sim_info_manager().get(away_action_tracker_proto.away_action.target_sim_id)
            else:
                target = None
            self._current_away_action = away_action_cls(self, target=target)

    def add_on_away_action_started_callback(self, callback):
        self._on_away_action_started.append(callback)

    def remove_on_away_action_started_callback(self, callback):
        self._on_away_action_started.remove(callback)

    def add_on_away_action_ended_callback(self, callback):
        self._on_away_action_ended.append(callback)

    def remove_on_away_action_ended_callback(self, callback):
        self._on_away_action_ended.remove(callback)
class BroadcasterService(Service):
    __qualname__ = 'BroadcasterService'
    INTERVAL = TunableRealSecond(description='\n        The time between broadcaster pulses. A lower number will impact\n        performance.\n        ', default=5)
    DEFAULT_QUADTREE_RADIUS = 0.1

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._alarm_handle = None
        self._processing_task = None
        self._on_update_callbacks = CallableList()
        self._pending_broadcasters = []
        self._active_broadcasters = []
        self._cluster_requests = {}
        self._object_cache = None
        self._pending_update = False
        self._quadtrees = defaultdict(sims4.geometry.QuadTree)

    def start(self):
        self._alarm_handle = add_alarm_real_time(self, interval_in_real_seconds(self.INTERVAL), self._on_update, repeating=True, use_sleep_time=False)
        object_manager = services.object_manager()
        object_manager.register_callback(CallbackTypes.ON_OBJECT_LOCATION_CHANGED, self._update_object_cache)
        object_manager.register_callback(CallbackTypes.ON_OBJECT_ADD, self._update_object_cache)
        services.current_zone().wall_contour_update_callbacks.append(self._update_object_cache)

    def stop(self):
        if self._alarm_handle is not None:
            cancel_alarm(self._alarm_handle)
            self._alarm_handle = None
        if self._processing_task is not None:
            self._processing_task.stop()
            self._processing_task = None
        object_manager = services.object_manager()
        object_manager.unregister_callback(CallbackTypes.ON_OBJECT_LOCATION_CHANGED, self._update_object_cache)
        object_manager.unregister_callback(CallbackTypes.ON_OBJECT_ADD, self._update_object_cache)
        services.current_zone().wall_contour_update_callbacks.remove(self._update_object_cache)

    def add_broadcaster(self, broadcaster):
        if broadcaster not in self._pending_broadcasters:
            self._pending_broadcasters.append(broadcaster)
            self._on_update_callbacks()

    def remove_broadcaster(self, broadcaster):
        if broadcaster in self._pending_broadcasters:
            self._pending_broadcasters.remove(broadcaster)
        if broadcaster in self._active_broadcasters:
            self._remove_from_cluster_request(broadcaster)
            self._remove_broadcaster_from_quadtree(broadcaster)
            self._active_broadcasters.remove(broadcaster)
        broadcaster.on_removed()
        self._on_update_callbacks()

    def _activate_pending_broadcasters(self):
        for broadcaster in self._pending_broadcasters:
            self._active_broadcasters.append(broadcaster)
            self.update_cluster_request(broadcaster)
            self._update_object_cache()
        self._pending_broadcasters.clear()

    def _add_broadcaster_to_quadtree(self, broadcaster):
        self._remove_broadcaster_from_quadtree(broadcaster)
        broadcaster_quadtree = self._quadtrees[broadcaster.routing_surface.secondary_id]
        broadcaster_bounds = sims4.geometry.QtCircle(sims4.math.Vector2(broadcaster.position.x, broadcaster.position.z), self.DEFAULT_QUADTREE_RADIUS)
        broadcaster_quadtree.insert(broadcaster, broadcaster_bounds)
        return broadcaster_quadtree

    def _remove_broadcaster_from_quadtree(self, broadcaster):
        broadcaster_quadtree = broadcaster.quadtree
        if broadcaster_quadtree is not None:
            broadcaster_quadtree.remove(broadcaster)

    def update_cluster_request(self, broadcaster):
        if broadcaster not in self._active_broadcasters:
            return
        clustering_request = broadcaster.get_clustering()
        if clustering_request is None:
            return
        self._remove_from_cluster_request(broadcaster)
        cluster_request_key = (type(broadcaster), broadcaster.routing_surface.secondary_id)
        if cluster_request_key in self._cluster_requests:
            cluster_request = self._cluster_requests[cluster_request_key]
            cluster_request.set_object_dirty(broadcaster)
        else:
            cluster_quadtree = self._quadtrees[broadcaster.routing_surface.secondary_id]
            cluster_request = clustering_request(lambda : self._get_broadcasters_for_cluster_request_gen(*cluster_request_key), quadtree=cluster_quadtree)
            self._cluster_requests[cluster_request_key] = cluster_request
        quadtree = self._add_broadcaster_to_quadtree(broadcaster)
        broadcaster.on_added_to_quadtree_and_cluster_request(quadtree, cluster_request)

    def _remove_from_cluster_request(self, broadcaster):
        cluster_request = broadcaster.cluster_request
        if cluster_request is not None:
            cluster_request.set_object_dirty(broadcaster)

    def _update_object_cache(self, obj=None):
        if obj is None:
            self._object_cache = None
            return
        if self._object_cache is not None:
            self._object_cache.add(obj)

    def _is_valid_broadcaster(self, broadcaster):
        broadcasting_object = broadcaster.broadcasting_object
        if broadcasting_object is None:
            return False
        if broadcasting_object.is_in_inventory():
            return False
        if broadcasting_object.parent is not None and broadcasting_object.parent.is_sim:
            return False
        return True

    def _get_broadcasters_for_cluster_request_gen(self, broadcaster_type, broadcaster_level):
        for broadcaster in self._active_broadcasters:
            while broadcaster.guid == broadcaster_type.guid:
                if broadcaster.should_cluster() and broadcaster.routing_surface.secondary_id == broadcaster_level:
                    yield broadcaster

    def get_broadcasters_gen(self, inspect_only=False):
        for (cluster_request_key, cluster_request) in self._cluster_requests.items():
            is_cluster_dirty = cluster_request.is_dirty()
            if is_cluster_dirty:
                for broadcaster in self._get_broadcasters_for_cluster_request_gen(*cluster_request_key):
                    broadcaster.regenerate_constraint()
            while not is_cluster_dirty or not inspect_only:
                while True:
                    for cluster in cluster_request.get_clusters_gen():
                        broadcaster_iter = cluster.objects_gen()
                        master_broadcaster = next(broadcaster_iter)
                        master_broadcaster.set_linked_broadcasters(list(broadcaster_iter))
                        yield master_broadcaster
        for broadcaster in self._active_broadcasters:
            while not broadcaster.should_cluster():
                if self._is_valid_broadcaster(broadcaster):
                    yield broadcaster

    def get_pending_broadcasters_gen(self):
        yield self._pending_broadcasters

    def _get_all_objects_gen(self):
        if any(broadcaster.allow_objects for broadcaster in self._active_broadcasters):
            if self._object_cache is None:
                self._object_cache = WeakSet(services.object_manager().valid_objects())
            yield list(self._object_cache)
        else:
            self._object_cache = None
            yield services.sim_info_manager().instanced_sims_gen()

    def register_callback(self, callback):
        if callback not in self._on_update_callbacks:
            self._on_update_callbacks.append(callback)

    def unregister_callback(self, callback):
        if callback in self._on_update_callbacks:
            self._on_update_callbacks.remove(callback)

    def _on_update(self, _):
        self._pending_update = True

    def update(self):
        if self._pending_update:
            self._pending_update = False
            self._update()

    def _update(self):
        try:
            self._activate_pending_broadcasters()
            current_broadcasters = set(self.get_broadcasters_gen())
            for obj in self._get_all_objects_gen():
                is_affected = False
                for broadcaster in current_broadcasters:
                    while broadcaster.can_affect(obj):
                        constraint = broadcaster.get_constraint()
                        if not constraint.valid:
                            pass
                        if constraint.geometry is None or constraint.geometry.contains_point(obj.position) and constraint.routing_surface == obj.routing_surface:
                            broadcaster.apply_broadcaster_effect(obj)
                            is_affected = True
                while not is_affected:
                    if self._object_cache is not None:
                        self._object_cache.remove(obj)
            for broadcaster in current_broadcasters:
                broadcaster.on_processed()
        finally:
            self._on_update_callbacks()
Esempio n. 47
0
class ObjectManager(DistributableObjectManager):
    __qualname__ = 'ObjectManager'
    FIREMETER_DISPOSABLE_OBJECT_CAP = Tunable(int, 5, description='Number of disposable objects a lot can have at any given moment.')
    BED_TAGS = TunableTuple(description='\n        Tags to check on an object to determine what type of bed an object is.\n        ', beds=TunableSet(description='\n            Tags that consider an object as a bed other than double beds.\n            ', tunable=TunableEnumWithFilter(tunable_type=tag.Tag, default=tag.Tag.INVALID, filter_prefixes=BED_PREFIX_FILTER)), double_beds=TunableSet(description='\n            Tags that consider an object as a double bed\n            ', tunable=TunableEnumWithFilter(tunable_type=tag.Tag, default=tag.Tag.INVALID, filter_prefixes=BED_PREFIX_FILTER)), kid_beds=TunableSet(description='\n            Tags that consider an object as a kid bed\n            ', tunable=TunableEnumWithFilter(tunable_type=tag.Tag, default=tag.Tag.INVALID, filter_prefixes=BED_PREFIX_FILTER)), other_sleeping_spots=TunableSet(description='\n            Tags that considered sleeping spots.\n            ', tunable=TunableEnumWithFilter(tunable_type=tag.Tag, default=tag.Tag.INVALID, filter_prefixes=BED_PREFIX_FILTER)))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._crafting_cache = CraftingObjectCache()
        self._sim_spawn_conditions = collections.defaultdict(set)
        self._client_connect_callbacks = CallableList()
        self._portal_cache = WeakSet()
        self._portal_added_callbacks = CallableList()
        self._portal_removed_callbacks = CallableList()
        self._front_door_candidates_changed_callback = CallableList()
        self._all_bed_tags = self.BED_TAGS.beds | self.BED_TAGS.double_beds | self.BED_TAGS.kid_beds | self.BED_TAGS.other_sleeping_spots

    @property
    def crafting_cache(self):
        return self._crafting_cache

    def portal_cache_gen(self):
        yield self._portal_cache

    def on_client_connect(self, client):
        all_objects = list(self._objects.values())
        for game_object in all_objects:
            game_object.on_client_connect(client)

    def move_to_inventory(self, obj, inventory_manager):
        del self._objects[obj.id]
        obj.manager = inventory_manager
        inventory_manager._objects[obj.id] = obj
        if hasattr(obj, 'on_added_to_inventory'):
            obj.on_added_to_inventory()

    def add(self, obj, *args, **kwargs):
        super().add(obj, *args, **kwargs)
        if obj.objectage_component is None:
            current_zone = services.current_zone()
            current_zone.increment_object_count(obj)
            current_zone.household_manager.increment_household_object_count(obj.get_household_owner_id())

    def remove(self, obj, *args, **kwargs):
        super().remove(obj, *args, **kwargs)
        if obj.objectage_component is None:
            current_zone = services.current_zone()
            current_zone.decrement_object_count(obj)
            current_zone.household_manager.decrement_household_object_count(obj.get_household_owner_id())

    def _should_save_object_on_lot(self, obj):
        parent = obj.parent
        if parent is not None and parent.is_sim:
            if obj.can_go_in_inventory_type(InventoryType.SIM):
                return False
        return True

    def pre_save(self):
        all_objects = list(self._objects.values())
        lot = services.current_zone().lot
        for (_, inventory) in lot.get_all_object_inventories_gen():
            for game_object in inventory:
                all_objects.append(game_object)
        for game_object in all_objects:
            game_object.update_all_commodities()

    def save(self, object_list=None, zone_data=None, open_street_data=None, **kwargs):
        if object_list is None:
            return
        open_street_objects = file_serialization.ObjectList()
        total_beds = 0
        double_bed_exist = False
        kid_bed_exist = False
        alternative_sleeping_spots = 0
        for game_object in self._objects.values():
            while self._should_save_object_on_lot(game_object):
                if game_object.persistence_group == objects.persistence_groups.PersistenceGroups.OBJECT:
                    save_result = game_object.save_object(object_list.objects, ItemLocation.ON_LOT, 0)
                else:
                    if game_object.item_location == ItemLocation.ON_LOT or game_object.item_location == ItemLocation.INVALID_LOCATION:
                        item_location = ItemLocation.FROM_OPEN_STREET
                    else:
                        item_location = game_object.item_location
                    save_result = game_object.save_object(open_street_objects.objects, item_location, 0)
                if not save_result:
                    pass
                if zone_data is None:
                    pass
                def_build_buy_tags = game_object.definition.build_buy_tags
                if not def_build_buy_tags & self._all_bed_tags:
                    pass
                if def_build_buy_tags & self.BED_TAGS.double_beds:
                    double_bed_exist = True
                    total_beds += 1
                elif def_build_buy_tags & self.BED_TAGS.kid_beds:
                    total_beds += 1
                    kid_bed_exist = True
                elif def_build_buy_tags & self.BED_TAGS.other_sleeping_spots:
                    alternative_sleeping_spots += 1
                elif def_build_buy_tags & self.BED_TAGS.beds:
                    total_beds += 1
        if open_street_data is not None:
            open_street_data.objects = open_street_objects
        if zone_data is not None:
            bed_info_data = gameplay_serialization.ZoneBedInfoData()
            bed_info_data.num_beds = total_beds
            bed_info_data.double_bed_exist = double_bed_exist
            bed_info_data.kid_bed_exist = kid_bed_exist
            bed_info_data.alternative_sleeping_spots = alternative_sleeping_spots
            zone_data.gameplay_zone_data.bed_info_data = bed_info_data
        lot = services.current_zone().lot
        for (inventory_type, inventory) in lot.get_all_object_inventories_gen():
            for game_object in inventory:
                game_object.save_object(object_list.objects, ItemLocation.OBJECT_INVENTORY, inventory_type)

    def valid_objects(self):
        return [obj for obj in self._objects.values() if not obj._hidden_flags]

    def get_objects_of_type_gen(self, *definitions):
        for obj in self._objects.values():
            while any(obj.definition is d for d in definitions):
                yield obj

    def get_objects_with_tag_gen(self, tag):
        for obj in self._objects.values():
            while build_buy.get_object_has_tag(obj.definition.id, tag):
                yield obj

    def add_sim_spawn_condition(self, sim_id, callback):
        for sim in services.sim_info_manager().instanced_sims_gen():
            while sim.id == sim_id:
                logger.error('Sim {} is already in the world, cannot add the spawn condition', sim)
                return
        self._sim_spawn_conditions[sim_id].add(callback)

    def remove_sim_spawn_condition(self, sim_id, callback):
        if callback not in self._sim_spawn_conditions.get(sim_id, ()):
            logger.error('Trying to remove sim spawn condition with invalid id-callback pair ({}-{}).', sim_id, callback)
            return
        self._sim_spawn_conditions[sim_id].remove(callback)

    def trigger_sim_spawn_condition(self, sim_id):
        if sim_id in self._sim_spawn_conditions:
            for callback in self._sim_spawn_conditions[sim_id]:
                callback()
            del self._sim_spawn_conditions[sim_id]

    def register_portal_added_callback(self, callback):
        if callback not in self._portal_added_callbacks:
            self._portal_added_callbacks.append(callback)

    def unregister_portal_added_callback(self, callback):
        if callback in self._portal_added_callbacks:
            self._portal_added_callbacks.remove(callback)

    def register_portal_removed_callback(self, callback):
        if callback not in self._portal_removed_callbacks:
            self._portal_removed_callbacks.append(callback)

    def unregister_portal_removed_callback(self, callback):
        if callback in self._portal_removed_callbacks:
            self._portal_removed_callbacks.remove(callback)

    def add_portal_to_cache(self, portal):
        self._portal_cache.add(portal)
        self._portal_added_callbacks(portal)

    def remove_portal_from_cache(self, portal):
        self._portal_cache.remove(portal)
        self._portal_removed_callbacks(portal)

    def register_front_door_candidates_changed_callback(self, callback):
        if callback not in self._front_door_candidates_changed_callback:
            self._front_door_candidates_changed_callback.append(callback)

    def unregister_front_door_candidates_changed_callback(self, callback):
        if callback in self._front_door_candidates_changed_callback:
            self._front_door_candidates_changed_callback.remove(callback)

    def on_front_door_candidates_changed(self):
        self._front_door_candidates_changed_callback()

    def advertising_objects_gen(self, motives:set=DEFAULT):
        if not motives:
            return
        if motives is DEFAULT:
            for obj in self.valid_objects():
                while obj.commodity_flags:
                    yield obj
            return
        for obj in self.valid_objects():
            while obj.commodity_flags & motives:
                yield obj

    def get_all_objects_with_component_gen(self, component):
        if component is None:
            return
        for obj in self.valid_objects():
            if obj.has_component(component.instance_attr):
                yield obj
            else:
                while obj.has_component(component.class_attr):
                    yield obj

    def on_location_changed(self, obj):
        self._registered_callbacks[CallbackTypes.ON_OBJECT_LOCATION_CHANGED](obj)

    @classproperty
    def supports_parenting(self):
        return True
Esempio n. 48
0
class SituationGoal(metaclass=HashedTunedInstanceMetaclass, manager=services.get_instance_manager(sims4.resources.Types.SITUATION_GOAL)):
    __qualname__ = 'SituationGoal'
    INSTANCE_SUBCLASSES_ONLY = True
    IS_TARGETED = False
    INSTANCE_TUNABLES = {'_display_name': TunableLocalizedStringFactory(description='\n                Display name for the Situation Goal. It takes one token, the\n                target (0) of this situation goal.\n                ', tuning_group=GroupNames.UI), '_icon': TunableResourceKey('PNG:missing_image', description='\n                Icon to be displayed for the Situation Goal.\n                ', needs_tuning=True, resource_types=sims4.resources.CompoundTypes.IMAGE), '_pre_tests': TunableSituationGoalPreTestSet(description='\n                A set of tests on the player sim and environment that all must\n                pass for the goal to be given to the player. e.g. Player Sim\n                has cooking skill level 7.\n                ', tuning_group=GroupNames.TESTS), '_post_tests': TunableSituationGoalPostTestSet(description='\n                A set of tests that must all pass when the player satisfies the\n                goal_test for the goal to be consider completed. e.g. Player\n                has Drunk Buff when Kissing another sim at Night.\n                ', tuning_group=GroupNames.TESTS), '_environment_pre_tests': TunableSituationGoalEnvironmentPreTestSet(description='\n                A set of sim independent pre tests.\n                e.g. There are five desks.\n                ', tuning_group=GroupNames.TESTS), 'role_tags': TunableSet(TunableEnumEntry(Tag, Tag.INVALID), description='\n                This goal will only be given to Sims in SituationJobs or Role\n                States marked with one of these tags.\n                '), '_cooldown': TunableSimMinute(description='\n                The cooldown of this situation goal.  Goals that have been\n                completed will not be chosen again for the amount of time that\n                is tuned.\n                ', default=600, minimum=0), '_iterations': Tunable(description='\n                     Number of times the player must perform the action to complete the goal\n                     ', tunable_type=int, default=1), '_score': Tunable(description='\n                    The number of points received for completing the goal.\n                    ', tunable_type=int, default=10), '_goal_loot_list': TunableList(description='\n            A list of pre-defined loot actions that will applied to every\n            sim in the situation when this situation goal is completed.\n             \n            Do not use this loot list in an attempt to undo changes made by\n            the RoleStates to the sim. For example, do not attempt\n            to remove buffs or commodities added by the RoleState.\n            ', tunable=SituationGoalLootActions.TunableReference()), '_tooltip': TunableLocalizedStringFactory(description='\n            Tooltip for this situation goal. It takes one token, the\n            actor (0) of this situation goal.'), 'noncancelable': Tunable(description='\n            Checking this box will prevent the player from canceling this goal in the whim system.', tunable_type=bool, needs_tuning=True, default=False), 'time_limit': Tunable(description='\n            Timeout (in Sim minutes) for Sim to complete this goal. The default state of 0 means\n            time is unlimited. If the goal is not completed in time, any tuned penalty loot is applied.', tunable_type=int, default=0), 'penalty_loot_list': TunableList(description='\n            A list of pre-defined loot actions that will applied to the Sim who fails\n            to complete this goal within the tuned time limit.\n            ', tunable=SituationGoalLootActions.TunableReference()), 'goal_completion_notification': OptionalTunable(tunable=UiDialogNotification.TunableFactory(description='\n                A TNS that will fire when this situation goal is completed.\n                ')), 'audio_sting_on_complete': TunableResourceKey(description='\n            The sound to play when this goal is completed.\n            ', default=None, resource_types=(sims4.resources.Types.PROPX,), tuning_group=GroupNames.AUDIO), 'goal_completion_modal_dialog': OptionalTunable(tunable=UiDialogOk.TunableFactory(description='\n                A modal dialog that will fire when this situation goal is\n                completed.\n                '))}

    @classmethod
    def can_be_given_as_goal(cls, actor, situation, **kwargs):
        if actor is not None:
            resolver = event_testing.resolver.DataResolver(actor.sim_info, None)
            result = cls._pre_tests.run_tests(resolver)
            if not result:
                return result
        environment_test_result = cls._environment_pre_tests.run_tests(Resolver())
        if not environment_test_result:
            return environment_test_result
        return TestResult.TRUE

    def __init__(self, sim_info=None, situation=None, goal_id=0, count=0, **kwargs):
        self._sim_info = sim_info
        self._situation = situation
        self.id = goal_id
        self._on_goal_completed_callbacks = CallableList()
        self._completed_time = None
        self._count = count

    def destroy(self):
        self.decommision()
        self._sim_info = None
        self._situation = None

    def decommision(self):
        self._on_goal_completed_callbacks.clear()

    def create_seedling(self):
        actor_id = 0 if self._sim_info is None else self._sim_info.sim_id
        seedling = situations.situation_serialization.GoalSeedling(type(self), actor_id, self._count)
        return seedling

    def register_for_on_goal_completed_callback(self, listener):
        self._on_goal_completed_callbacks.append(listener)

    def unregister_for_on_goal_completed_callback(self, listener):
        self._on_goal_completed_callbacks.remove(listener)

    def get_gsi_name(self):
        if self._iterations <= 1:
            return self.__class__.__name__
        return '{} {}/{}'.format(self.__class__.__name__, self._count, self._iterations)

    def _on_goal_completed(self):
        self._completed_time = services.time_service().sim_now
        for loots in self._goal_loot_list:
            for loot in loots.goal_loot_actions:
                for sim in self._situation.all_sims_in_situation_gen():
                    loot.apply_to_resolver(sim.get_resolver())
        client = services.client_manager().get_first_client()
        if client is not None:
            active_sim = client.active_sim
            resolver = SingleSimResolver(active_sim)
            if self.goal_completion_notification is not None:
                notification = self.goal_completion_notification(active_sim, resolver=resolver)
                notification.show_dialog()
            if self.goal_completion_modal_dialog is not None:
                dialog = self.goal_completion_modal_dialog(active_sim, resolver=resolver)
                dialog.show_dialog()
        self._on_goal_completed_callbacks(self, True)

    def _on_iteration_completed(self):
        self._on_goal_completed_callbacks(self, False)

    def debug_force_complete(self, target_sim):
        self._count = self._iterations
        self._on_goal_completed()

    def handle_event(self, sim_info, event, resolver):
        if self._sim_info is not None and self._sim_info is not sim_info:
            return
        if self._run_goal_completion_tests(sim_info, event, resolver):
            if self._count >= self._iterations:
                self._on_goal_completed()
            else:
                self._on_iteration_completed()

    def _run_goal_completion_tests(self, sim_info, event, resolver):
        return self._post_tests.run_tests(resolver)

    def _get_actual_target_sim_info(self):
        pass

    def get_required_target_sim_info(self):
        pass

    @property
    def created_time(self):
        pass

    @property
    def completed_time(self):
        return self._completed_time

    def is_on_cooldown(self):
        if self._completed_time is None:
            return False
        time_since_last_completion = services.time_service().sim_now - self._completed_time
        return time_since_last_completion < interval_in_sim_minutes(self._cooldown)

    def get_localization_tokens(self):
        if self._sim_info is None:
            return (self._numerical_token,)
        required_tgt_sim = self.get_required_target_sim_info()
        if required_tgt_sim is None:
            return (self._numerical_token, self._sim_info)
        return (self._numerical_token, self._sim_info, required_tgt_sim)

    @property
    def display_name(self):
        return self._display_name(*self.get_localization_tokens())

    @property
    def tooltip(self):
        return self._tooltip(*self.get_localization_tokens())

    @property
    def score(self):
        return self._score

    @property
    def completed_iterations(self):
        return self._count

    @property
    def max_iterations(self):
        return self._iterations

    @property
    def _numerical_token(self):
        return self.max_iterations