def __init__(self, owner=None): self._statistics = {} self._owner = owner self._listeners = {} self._watchers = {} self._on_remove_callbacks = sims4.callback_utils.CallableList() self._handle_id_gen_listeners = uid.UniqueIdGenerator(1) self._handle_id_gen_watchers = uid.UniqueIdGenerator(1) self.suppress_callback_setup_during_load = False
def __init__(self, situation): self._situation = situation self._realized_main_goal = None self._main_goal_completed = False self._realized_minor_goals = {} self._goal_chains = None self._inherited_target_sim_info = None self._goal_id_generator = uid.UniqueIdGenerator(1) self._has_offered_goals = False self._completed_goals = {}
def __init__(self, sim_info): self._sim_info = sim_info self._goal_id_generator = uid.UniqueIdGenerator(1) self._active_whimsets_data = {} self._active_whims = [_ActiveWhimData() for _ in range(self.max_whims)] self._hidden = False self._cooldown_alarms = {} self._whim_goal_proto = None self._completed_goals = {} self._test_results_map = {} self._goals_dirty = True self._score_multipliers = []
def __init__(self, owner): super().__init__(owner) self._get_next_statistic_handle = uid.UniqueIdGenerator(1) self._statistic_modifiers = {} self._locked_commodities = {} self._relationship_score_multiplier_with_buff_on_target = None self._commodity_tracker = None self._static_commodity_tracker = None self._statistic_tracker = None self._commodity_distress_refs = [] self._commodities_added = {} self._interaction_modifiers = {} self._suspended_modifiers = {}
def __init__(self, sim_info): self._sim_info = sim_info self._goal_id_generator = uid.UniqueIdGenerator(1) self._whim_goal_proto = None self._active_whims = [] for _ in range(WhimsTracker.MAX_GOALS + 1): self._active_whims.append(None) self._realized_goals = {} self._whimset_target_map = {} self._completed_goals = {} self._whimset_objective_map = {} self._test_results_map = {} self.active_sets = [] self.active_chained_sets = [] self.sets_on_cooldown = [] self.alarm_handles = {} self.delay_alarm_handles = [] self._goals_dirty = True
def __init__(self, owner): super().__init__(owner) self._get_next_statistic_handle = uid.UniqueIdGenerator(1) self._statistic_modifiers = {} self._locked_commodities = {} self._relationship_score_multiplier_with_buff_on_target = defaultdict( list) self._object_tags_that_override_off_lot_autonomy_ref_count = collections.defaultdict( int) self._situation_type_social_score_multipliers = defaultdict(list) self._sit_posture_transition_penalties = [] self._outside_object_supression = None self._commodity_tracker = None self._static_commodity_tracker = None self._statistic_tracker = None self._commodity_distress_refs = [] self._commodities_added = {} self._saved_commodities_value_for_travel = {} self._interaction_modifiers = {} self._suspended_modifiers = {} self._interaction_score_modifier = [] self._ui_skill_bar_suppression_count = 0
class ClientObjectMixin: INITIAL_DEPRECIATION = TunablePercent( 20, description= 'Amount (0%%-100%%) of depreciation to apply to an object after purchase. An item worth 10 in the catalog if tuned at 20%% will be worth 8 after purchase.' ) FADE_DURATION = TunableSimMinute( 1.2, description='Default fade time (in sim minutes) for objects.') VISIBLE_TO_AUTOMATION = True _get_next_ui_metadata_handle = uid.UniqueIdGenerator(min_uid=1) HOVERTIP_HANDLE = 0 ui_metadata = distributor.sparse.SparseField( ui_protocols.UiObjectMetadata, distributor.ops.SetUiObjectMetadata) _generic_ui_metadata_setters = {} FORWARD_OFFSET = 0.04 def __init__(self, definition, **kwargs): super().__init__(definition, **kwargs) if definition is not None: self.apply_definition(definition, **kwargs) self._ui_metadata_stack = None self._ui_metadata_handles = None self._ui_metadata_cache = None self.primitives = distributor.ops.DistributionSet(self) zone_id = services.current_zone_id() self._location = sims4.math.Location( sims4.math.Transform(), routing.SurfaceIdentifier(zone_id, 0, routing.SurfaceType.SURFACETYPE_WORLD)) self._children_objects = None self._scale = 1 self._parent_type = ObjectParentType.PARENT_NONE self._parent_location = 0 self._build_buy_lockout = False self._build_buy_lockout_alarm_handler = None self._tint = None self._opacity = None self._censor_state = None self._geometry_state = None self._geometry_state_overrides = None self._standin_model = None self._visibility = None self._visibility_flags = None self._material_state = None self._reference_arb = None self._audio_effects = None self._video_playlist = None self._painting_state = None self.custom_name = None self.custom_description = None self._multicolor = None self._display_number = None self._awareness_scores = None self._scratched = False self._base_value = definition.price self._needs_post_bb_fixup = False self._needs_depreciation = False self._swapping_to_parent = None self._swapping_from_parent = None self._on_children_changed = None self.allow_opacity_change = True self._wind_speed_effect = None def get_create_op(self, *args, **kwargs): additional_ops = list(self.get_additional_create_ops_gen()) return distributor.ops.ObjectCreate(self, *args, additional_ops=additional_ops, **kwargs) @forward_to_components_gen def get_additional_create_ops_gen(self): pass def get_create_after_objs(self): parent = self.parent_object(child_type=ChildrenType.BB_ONLY) if parent is not None: return (parent, ) return () def get_delete_op(self, fade_duration=0): return distributor.ops.ObjectDelete(fade_duration=fade_duration) @forward_to_components def apply_definition(self, definition, obj_state=0): if not isinstance(definition, objects.definition.Definition): definition = services.definition_manager().get(definition) self._model = definition.get_model(obj_state) self._material_variant = definition.material_variant self._rig = definition.get_rig(obj_state) self._slot = definition.get_slot(obj_state) self._slots_resource = definition.get_slots_resource(obj_state) self._state_index = obj_state def set_definition(self, definition_id, ignore_rig_footprint=False): new_definition = services.definition_manager().get(definition_id) (result, error) = self.definition.is_similar( new_definition, ignore_rig_footprint=ignore_rig_footprint) if not result: logger.error( 'Trying to set the definition {} to an incompatible definition {}.\n {}', self.definition.id, definition_id, error, owner='nbaker') return False services.definition_manager().unregister_definition( self.definition.id, self) self.apply_definition(new_definition, self._state_index) self.definition = new_definition services.definition_manager().register_definition( new_definition.id, self) self.resend_model_with_material_variant() self.resend_slot() self.resend_state_index() op = distributor.ops.SetObjectDefinitionId(definition_id) distributor.system.Distributor.instance().add_op(self, op) return True @property def hover_tip(self): if self._ui_metadata_stack is None or ClientObjectMixin.HOVERTIP_HANDLE not in self._ui_metadata_handles: return (_, _, value) = self._ui_metadata_handles[ClientObjectMixin.HOVERTIP_HANDLE] return value @hover_tip.setter def hover_tip(self, value): if value is not None: if self._ui_metadata_stack is None: self._ui_metadata_stack = [] self._ui_metadata_handles = {} self._ui_metadata_cache = {} data = self._ui_metadata_handles.get( ClientObjectMixin.HOVERTIP_HANDLE) if data is not None: self._ui_metadata_stack.remove(data) data = (ClientObjectMixin.HOVERTIP_HANDLE, 'hover_tip', value) self._ui_metadata_stack.append(data) self._ui_metadata_handles[ClientObjectMixin.HOVERTIP_HANDLE] = data def add_ui_metadata(self, name, value): if self._ui_metadata_stack is None: self._ui_metadata_stack = [] self._ui_metadata_handles = {} self._ui_metadata_cache = {} if name not in self._ui_metadata_cache: default_value = type(self).ui_metadata.generic_getter(name)(self) self._ui_metadata_cache[name] = default_value handle = self._get_next_ui_metadata_handle() data = (handle, name, value) self._ui_metadata_stack.append(data) self._ui_metadata_handles[handle] = data return handle def get_ui_metadata(self, handle): return self._ui_metadata_handles[handle] def remove_ui_metadata(self, handle): if self._ui_metadata_stack is not None: self._ui_metadata_stack.remove(self._ui_metadata_handles[handle]) def update_ui_metadata(self, use_cache=True): if self._ui_metadata_stack is None: return ui_metadata = {} for (_, name, value) in self._ui_metadata_stack: ui_metadata[name] = value for (name, value) in ui_metadata.items(): if name in self._ui_metadata_cache and self._ui_metadata_cache[ name] == value and use_cache: continue if name in self._generic_ui_metadata_setters: setter = self._generic_ui_metadata_setters[name] else: setter = type(self).ui_metadata.generic_setter(name, auto_reset=True) self._generic_ui_metadata_setters[name] = setter try: setter(self, value) except (ValueError, TypeError): logger.error( 'Error trying to set field {} to value {} in object {}.', name, value, self, owner='camilogarcia') for name in self._ui_metadata_cache.keys() - ui_metadata.keys(): try: if name in self._generic_ui_metadata_setters: self._generic_ui_metadata_setters[name](self, None) except (ValueError, TypeError): logger.error( 'Error trying to set field {} to default in object {}.', name, self, owner='nabaker') self._ui_metadata_cache = ui_metadata @property def swapping_to_parent(self): return self._swapping_to_parent @property def swapping_from_parent(self): return self._swapping_from_parent @contextmanager def _swapping_parents(self, old_parent, new_parent): self._swapping_from_parent = old_parent self._swapping_to_parent = new_parent try: yield None finally: self._swapping_from_parent = None self._swapping_to_parent = None @property def location(self): return self._location @distributor.fields.Field(op=distributor.ops.SetLocation) def _location_field_internal(self): return self resend_location = _location_field_internal.get_resend() @location.setter def location(self, new_location): self.set_location_without_distribution(new_location) self.resend_location() def set_location_without_distribution(self, new_location): if not isinstance(new_location, sims4.math.Location): raise TypeError() if not (new_location == self._location and (self.parts is not None and new_location.parent is not None) and new_location.parent.parts is not None): return old_location = self._location events = [(self, old_location)] for child in self.get_all_children_recursive_gen(): events.append((child, child._location)) if new_location.parent != old_location.parent: self.pre_parent_change(new_location.parent) with self._swapping_parents(old_location.parent, new_location.parent): if old_location.parent is not None: old_location.parent._remove_child( self, new_parent=new_location.parent) if new_location.parent is not None: new_location.parent._add_child(self, new_location) visibility_state = self.visibility or VisibilityState() if new_location.parent is not None and new_location.parent._disable_child_footprint_and_shadow: visibility_state.enable_drop_shadow = False else: visibility_state.enable_drop_shadow = True self.visibility = visibility_state if new_location.parent is not None: current_inventory = self.get_inventory() if current_inventory is not None and not current_inventory.try_remove_object_by_id( self.id): raise RuntimeError( 'Unable to remove object: {} from the inventory: {}, parenting request will be ignored.' .format(self, current_inventory)) posture_graph_service = services.current_zone().posture_graph_service with posture_graph_service.object_moving(self): self._location = new_location if self.parts: for part in self.parts: part.on_owner_location_changed() if new_location.parent != old_location.parent: self.on_parent_change(new_location.parent) for (obj, old_value) in events: if obj is not self: new_location = obj.location.clone() obj._location = new_location obj.on_location_changed(old_value) def set_location(self, location): self.location = location def move_to(self, **overrides): self.location = self._location.clone(**overrides) @distributor.fields.Field(op=distributor.ops.SetAudioEffects) def audio_effects(self): return self._audio_effects resend_audio_effects = audio_effects.get_resend() def append_audio_effect(self, key, audio_effect_data): if self._audio_effects is None: self._audio_effects = {} self._audio_effects[key] = audio_effect_data self.resend_audio_effects() def remove_audio_effect(self, key): if self._audio_effects is None: logger.error( 'Found audio effects is None while trying to remove audio effect with key {} on {}', key, self, owner='jdimailig') return if key in self._audio_effects: del self._audio_effects[key] if not self._audio_effects: self._audio_effects = None self.resend_audio_effects() @forward_to_components def on_location_changed(self, old_location): pass @property def transform(self): return self._location.world_transform @transform.setter def transform(self, transform): if self.parent is not None: self.move_to(transform=transform, parent=None, routing_surface=self.parent.routing_surface) return self.move_to(transform=transform) @property def position(self): return self.transform.translation @property def position_with_forward_offset(self): return self.position + self.forward * ClientObjectMixin.FORWARD_OFFSET @property def intended_position_with_forward_offset(self): return self.intended_position + self.intended_forward * ClientObjectMixin.FORWARD_OFFSET @property def orientation(self): return self.transform.orientation @property def forward(self): return self.orientation.transform_vector( self.forward_direction_for_picking) @property def routing_surface(self): return self._location.world_routing_surface @property def level(self): routing_surface = self.routing_surface if routing_surface is None: return return routing_surface.secondary_id @property def routing_location(self): return self.get_routing_location_for_transform(self.transform) def get_routing_location_for_transform(self, transform, routing_surface=DEFAULT): routing_surface = self.routing_surface if routing_surface is DEFAULT else routing_surface return routing.Location(transform.translation, transform.orientation, routing_surface) @property def intended_transform(self): return self.transform @property def intended_position(self): return self.intended_transform.translation @property def intended_forward(self): return self.intended_transform.orientation.transform_vector( self.forward_direction_for_picking) @property def intended_routing_surface(self): return self.routing_surface @property def parent(self): parent = self._location.parent parent = self.attempt_to_remap_parent(parent) if parent is not None: children = parent.children if not (self not in children and self.is_part and self.part_owner in children): return return parent @property def bb_parent(self): return self._location.parent def attempt_to_remap_parent(self, parent): if self.is_part and parent is not None and parent.parts is not None: distance = None found_part = None for part in parent.parts: dot = sims4.math.vector_dot(self.forward, part.forward) if dot < -0.98: new_distance = (part.position - self.position).magnitude_squared() if not distance is None: if new_distance <= distance: if new_distance == distance and found_part.subroot_index is not None: continue distance = new_distance found_part = part if new_distance == distance and found_part.subroot_index is not None: continue distance = new_distance found_part = part return found_part return parent def parent_object(self, child_type=ChildrenType.DEFAULT): if child_type is ChildrenType.BB_ONLY: parent = self.bb_parent else: parent = self.parent if parent is not None: if parent.is_part: parent = parent.part_owner return parent @property def parent_slot(self): parent = self._location.parent if parent is None: return bone_name_hash = self._location.joint_name_or_hash or self._location.slot_hash result = None for runtime_slot in parent.get_runtime_slots_gen( bone_name_hash=bone_name_hash): assert not result is not None result = runtime_slot if result is None: result = RuntimeSlot(parent, bone_name_hash, frozenset()) return result def get_parenting_root(self): result = self next_parent = result.parent while next_parent is not None: result = next_parent next_parent = result.parent return result @property def children(self): if self._children_objects is not None: return self._children_objects[ChildrenType.DEFAULT] return () def children_recursive_gen(self, include_self=False): if include_self: yield self for child in self.children: yield child for grandchild in child.children_recursive_gen(): yield grandchild @assertions.hot_path def _children_recursive_fast_gen(self): yield self for child in self.children: yield from child._children_recursive_fast_gen() def get_all_children_gen(self): if self._children_objects is not None: for children in self._children_objects.values(): yield from children def get_all_children_recursive_gen(self): for child in self.get_all_children_gen(): yield child yield from child.get_all_children_recursive_gen() def clear_default_children(self): if self._children_objects is not None: self._children_objects[ChildrenType.DEFAULT].clear() @assertions.hot_path def parenting_hierarchy_gen(self): self_parent = self.parent if self_parent is not None: master_parent = self_parent master_parent_parent = master_parent.parent while master_parent_parent is not None: master_parent = master_parent_parent master_parent_parent = master_parent.parent yield from master_parent._children_recursive_fast_gen() else: yield from self._children_recursive_fast_gen() def on_reset_send_op(self, reset_reason): super().on_reset_send_op(reset_reason) if self.valid_for_distribution: if reset_reason != ResetReason.BEING_DESTROYED or self.vehicle_component is not None: try: reset_op = distributor.ops.ResetObject(self.id) dist = Distributor.instance() dist.add_op(self, reset_op) except: logger.exception( 'Exception thrown sending reset op for {}', self) def on_reset_internal_state(self, reset_reason): if self.valid_for_distribution and reset_reason != ResetReason.BEING_DESTROYED: self.geometry_state = None self.material_state = None self.resend_location() self._reset_reference_arb() super().on_reset_internal_state(reset_reason) def on_reset_get_interdependent_reset_records(self, reset_reason, reset_records): super().on_reset_get_interdependent_reset_records( reset_reason, reset_records) for child in set(self.get_all_children_gen()): reset_records.append( ResetRecord(child, ResetReason.RESET_EXPECTED, self, 'Child')) @property def slot_hash(self): return self._location.slot_hash @slot_hash.setter def slot_hash(self, value): if self._location.slot_hash != value: self.location = self._location.clone(slot_hash=value) @property def bone_name_hash(self): return self._location.joint_name_or_hash or self._location.slot_hash @property def part_suffix(self) -> str: pass @distributor.fields.Field(op=distributor.ops.SetModel) def model_with_material_variant(self): return (self._model, self._material_variant) resend_model_with_material_variant = model_with_material_variant.get_resend( ) @model_with_material_variant.setter def model_with_material_variant(self, value): (self._model, self._material_variant) = value @property def model(self): return self._model @model.setter def model(self, value): model_res_key = None if isinstance(value, sims4.resources.Key): model_res_key = value elif isinstance(value, Definition): model_res_key = value.get_model(index=0) self.set_definition(value.id, ignore_rig_footprint=True) else: if value is not None: logger.error( 'Trying to set the model of object {} to the invalid value of {}. The object will revert to its default model instead.', self, value, owner='tastle') model_res_key = self.definition.get_model(self._state_index) self.model_with_material_variant = (model_res_key, self._material_variant) @property def material_variant(self): return self._material_variant @material_variant.setter def material_variant(self, value): if value is None: self.model_with_material_variant = (self._model, None) else: if not isinstance(value, str): raise TypeError('Model variant value must be a string') if not value: self.model_with_material_variant = (self._model, None) else: try: variant_value = int(value) except ValueError: variant_value = sims4.hash_util.hash32(value) self.model_with_material_variant = (self._model, variant_value) @distributor.fields.Field(op=distributor.ops.SetStandInModel) def standin_model(self): return self._standin_model @standin_model.setter def standin_model(self, value): self._standin_model = value @distributor.fields.Field(op=distributor.ops.SetObjectDefStateIndex, default=0) def state_index(self): return self._state_index resend_state_index = state_index.get_resend() @distributor.fields.Field(op=distributor.ops.SetRig, priority=distributor.fields.Field.Priority.HIGH) def rig(self): return self._rig @rig.setter def rig(self, value): if not isinstance(value, sims4.resources.Key): raise TypeError self._rig = value @distributor.fields.Field(op=distributor.ops.SetSlot) def slot(self): return self._slot resend_slot = slot.get_resend() @property def slots_resource(self): return self._slots_resource @distributor.fields.Field(op=distributor.ops.SetScale, default=1) def _client_scale(self): scale_value = self.scale for modifier in self.scale_modifiers_gen(): scale_value *= modifier return scale_value _resend_client_scale = _client_scale.get_resend() @property def scale(self): return self._scale @forward_to_components_gen def scale_modifiers_gen(self): pass @scale.setter def scale(self, value): if self._scale != value: self._scale = value self.on_location_changed(self._location) self._resend_client_scale() @property def parent_type(self): return self._parent_type @parent_type.setter def parent_type(self, value): self._parent_type = value self._resend_parent_type_info() @distributor.fields.Field(op=distributor.ops.SetParentType, default=None) def parent_type_info(self): return (self._parent_type, self._parent_location) @parent_type_info.setter def parent_type_info(self, value): (self._parent_type, self._parent_location) = value _resend_parent_type_info = parent_type_info.get_resend() @property def build_buy_lockout(self): return self._build_buy_lockout @distributor.fields.Field(op=distributor.ops.SetTint, default=None) def tint(self): if self.build_buy_lockout and lockout_visualization: return sims4.color.ColorARGB32(23782) return self._tint @tint.setter def tint(self, tint_color): value = getattr(tint_color, 'value', tint_color) if value and not isinstance(value, sims4.color.ColorARGB32): raise TypeError('Tint value must be a Color') if value == sims4.color.Color.WHITE: self._tint = None else: self._tint = value resend_tint = tint.get_resend() @distributor.fields.Field(op=distributor.ops.SetMulticolor, default=None) def multicolor(self): return self._multicolor @multicolor.setter def multicolor(self, value): self._multicolor = value resend_multicolor = multicolor.get_resend() @distributor.fields.Field(op=distributor.ops.SetDisplayNumber, default=None) def display_number(self): return self._display_number @display_number.setter def display_number(self, value): self._display_number = value resend_display_number = display_number.get_resend() def update_display_number(self, display_number=None): if display_number is not None: self.display_number = display_number return if hasattr(self, 'get_display_number'): self.display_number = self.get_display_number() @distributor.fields.Field(op=distributor.ops.SetOpacity, default=None) def opacity(self): return self._opacity @opacity.setter def opacity(self, value): if self.allow_opacity_change: self._opacity = self._clamp_opacity(value) def _clamp_opacity(self, value): if value is None: return try: value = float(value) except: raise TypeError('Opacity value must be a float') return sims4.math.clamp(0.0, value, 1.0) @distributor.fields.Field(op=SetAwarenessSourceOp) def awareness_scores(self): return self._awareness_scores resend_awareness_scores = awareness_scores.get_resend() def add_awareness_scores(self, awareness_sources): if self._awareness_scores is None: self._awareness_scores = Counter() self._awareness_scores.update(awareness_sources) self.resend_awareness_scores() def remove_awareness_scores(self, awareness_sources): if self._awareness_scores is None: return self._awareness_scores.subtract(awareness_sources) for awareness_channel in tuple(self.awareness_scores): if not self._awareness_scores[awareness_channel]: del self._awareness_scores[awareness_channel] if not self._awareness_scores: self._awareness_scores = None self.resend_awareness_scores() def add_geometry_state_override(self, original_geometry_state, override_geometry_state): if self._geometry_state_overrides is None: self._geometry_state_overrides = {} original_state_hash = sims4.hash_util.hash32(original_geometry_state) override_state_hash = sims4.hash_util.hash32(override_geometry_state) logger.assert_raise( original_state_hash not in self._geometry_state_overrides, 'add_geometry_state_override does not support multiple overrides per state' ) self._geometry_state_overrides[ original_state_hash] = override_state_hash self.geometry_state = self.geometry_state def remove_geometry_state_override(self, original_geometry_state): state_hash = sims4.hash_util.hash32(original_geometry_state) if state_hash in self._geometry_state_overrides: del self._geometry_state_overrides[state_hash] if not self._geometry_state_overrides: self._geometry_state_overrides = None @distributor.fields.Field(op=distributor.ops.SetGeometryState, default=None) def geometry_state(self): return self._geometry_state @geometry_state.setter def geometry_state(self, value): self._geometry_state = self._get_geometry_state_for_value(value) def _get_geometry_state_for_value(self, value): if not value: return if isinstance(value, str): state_hash = sims4.hash_util.hash32(value) elif isinstance(value, int): state_hash = value if self._geometry_state_overrides is not None: if state_hash in self._geometry_state_overrides: state_hash = self._geometry_state_overrides[state_hash] return state_hash @distributor.fields.Field(op=distributor.ops.SetCensorState, default=None) def censor_state(self): return self._censor_state @censor_state.setter def censor_state(self, value): try: value = CensorState(value) except: raise TypeError('Censor State value must be an int') self._censor_state = value @distributor.fields.Field(op=distributor.ops.SetVisibility, default=None) def visibility(self): return self._visibility @visibility.setter def visibility(self, value): if not isinstance(value, VisibilityState): raise TypeError( 'Visibility must be set to value of type VisibilityState') self._visibility = value if value is not None: if value.visibility is True: if value.inherits is False: if value.enable_drop_shadow is False: self._visibility = None @distributor.fields.Field(op=distributor.ops.SetVisibilityFlags) def visibility_flags(self): return self._visibility_flags @visibility_flags.setter def visibility_flags(self, value): self._visibility_flags = value @distributor.fields.Field(op=distributor.ops.SetMaterialState, default=None) def material_state(self): return self._material_state @material_state.setter def material_state(self, value): if value is None: self._material_state = None else: if not isinstance(value, MaterialState): raise TypeError( 'Material State must be set to value of type MaterialState' ) if value.state_name_hash == 0: self._material_state = None else: self._material_state = value @property def material_hash(self): if self.material_state is None: return 0 else: return self.material_state.state_name_hash @distributor.fields.Field(op=distributor.ops.StartArb, default=None) def reference_arb(self): return self._reference_arb def update_reference_arb(self, arb): if self._reference_arb is None: self._reference_arb = animation.arb.Arb() native.animation.update_post_condition_arb(self._reference_arb, arb) def _reset_reference_arb(self): if self._reference_arb is not None: reset_arb_element = ArbElement(animation.arb.Arb()) reset_arb_element.add_object_to_reset(self) reset_arb_element.distribute() reset_arb_element.cleanup() self._reference_arb = None _NO_SLOTS = EMPTY_SET @property def deco_slot_size(self): return get_object_decosize(self.definition.id) @property def deco_slot_types(self): return DecorativeSlotTuning.get_slot_types_for_object( self.deco_slot_size) @property def slot_type_set(self): key = get_object_slotset(self.definition.id) return get_slot_type_set_from_key(key) @property def slot_types(self): slot_type_set = self.slot_type_set if slot_type_set is not None: return slot_type_set.slot_types return self._NO_SLOTS @property def ideal_slot_types(self): carryable = self.get_component(CARRYABLE_COMPONENT) if carryable is not None: slot_type_set = carryable.ideal_slot_type_set if slot_type_set is not None: return slot_type_set.slot_types & (self.slot_types | self.deco_slot_types) return self._NO_SLOTS @property def all_valid_slot_types(self): return self.deco_slot_types | self.slot_types def _add_child(self, child, location): if self._children_objects is None: self._children_objects = defaultdict(WeakSet) if not isinstance(self.children, (WeakSet, set)): raise TypeError( "self.children is not a WeakSet or a set, it's {}".format( self.children)) bone_name_hash = location.joint_name_or_hash or location.slot_hash found_runtime_slot = None for runtime_slot in location.parent.get_runtime_slots_gen( bone_name_hash=bone_name_hash): assert not found_runtime_slot is not None found_runtime_slot = runtime_slot if found_runtime_slot is not None: for slot_type in found_runtime_slot.slot_types: if not slot_type.bb_only: self._children_objects[ChildrenType.DEFAULT].add(child) break else: self._children_objects[ChildrenType.BB_ONLY].add(child) else: self._children_objects[ChildrenType.DEFAULT].add(child) if self.parts: for part in self.parts: part.on_children_changed() self.on_child_added(child, location) def _remove_child(self, child, new_parent=None): if not isinstance(self.children, (WeakSet, set)): raise TypeError( "self.children is not a WeakSet or a set, it's {}".format( self.children)) for (_, weak_obj_set) in self._children_objects.items(): if child in weak_obj_set: weak_obj_set.discard(child) break if self.parts: for part in self.parts: part.on_children_changed() self.on_child_removed(child, new_parent=new_parent) @forward_to_components def on_remove_from_client(self): super().on_remove_from_client() for primitive in tuple(self.primitives): primitive.detach(self) def post_remove(self): super().post_remove() for primitive in tuple(self.primitives): primitive.detach(self) self.primitives = None @forward_to_components def on_child_added(self, child, location): if self._on_children_changed is None: return self._on_children_changed(child, location=location) @forward_to_components def on_child_removed(self, child, new_parent=None): if self._on_children_changed is None: return self._on_children_changed(child, new_parent=new_parent) @forward_to_components def pre_parent_change(self, parent): pass @forward_to_components def on_parent_change(self, parent): caches.clear_all_caches() if parent is None: self.parent_type = ObjectParentType.PARENT_NONE else: self.parent_type = ObjectParentType.PARENT_OBJECT def create_parent_location(self, parent, transform=sims4.math.Transform.IDENTITY(), joint_name_or_hash=None, slot_hash=0, routing_surface=None): if parent is not None: if self in parent.ancestry_gen(): raise ValueError( 'Invalid parent value (parent chain is circular)') if joint_name_or_hash: native.animation.get_joint_transform_from_rig( parent.rig, joint_name_or_hash) if slot_hash and not parent.has_slot(slot_hash): raise KeyError( 'Could not slot {}/{} in slot {} on {}/{}'.format( self, self.definition, hex(slot_hash), parent, parent.definition)) part_joint_name = joint_name_or_hash or slot_hash if parent is not None: if part_joint_name is not None: if not parent.is_part: if parent.parts: for part in parent.parts: if part.has_slot(part_joint_name): parent = part break new_location = self._location.clone( transform=transform, joint_name_or_hash=joint_name_or_hash, slot_hash=slot_hash, parent=parent, routing_surface=routing_surface) return new_location def set_parent(self, *args, **kwargs): new_location = self.create_parent_location(*args, **kwargs) self.location = new_location def clear_parent(self, transform, routing_surface): return self.set_parent(None, transform=transform, routing_surface=routing_surface) def remove_reference_from_parent(self): parent = self.bb_parent if parent is not None: parent._remove_child(self, new_parent=UNSET) @distributor.fields.Field(op=distributor.ops.VideoSetPlaylistOp, default=None) def video_playlist(self): return self._video_playlist @video_playlist.setter def video_playlist(self, playlist): self._video_playlist = playlist _resend_video_playlist = video_playlist.get_resend() def fade_opacity(self, opacity: float, duration: float, immediate=False, additional_channels=None): if self.allow_opacity_change: opacity = self._clamp_opacity(opacity) if opacity != self._opacity: self._opacity = opacity fade_op = distributor.ops.FadeOpacity(opacity, duration, immediate=immediate) if additional_channels: for channel in additional_channels: fade_op.add_additional_channel(*channel) distributor.ops.record(self, fade_op) def fade_in(self, fade_duration=None, immediate=False, additional_channels=None): if self.allow_opacity_change: if fade_duration is None: fade_duration = ClientObjectMixin.FADE_DURATION if self.visibility is not None: if not self.visibility.visibility: self.visibility = VisibilityState() self.opacity = 0 self.fade_opacity(1, fade_duration, immediate=immediate, additional_channels=additional_channels) def fade_out(self, fade_duration=None, immediate=False, additional_channels=None): if self.allow_opacity_change: if fade_duration is None: fade_duration = ClientObjectMixin.FADE_DURATION self.fade_opacity(0, fade_duration, immediate=immediate, additional_channels=additional_channels) @distributor.fields.Field(op=distributor.ops.SetValue, default=None) def current_value(self): new_value = self._base_value statistic_component = self.statistic_component if statistic_component is not None: new_value += statistic_component.get_added_monetary_value() state_component = self.state_component if state_component is not None: return max( round(new_value * state_component.state_based_value_mod), 0) return max(new_value, 0) @current_value.setter def current_value(self, value): state_component = self.state_component if state_component is not None: self.base_value = value / state_component.state_based_value_mod else: self.base_value = value _resend_current_value = current_value.get_resend() def update_current_value(self, update_tooltip=True): self._resend_current_value() if update_tooltip: self.update_tooltip_field(TooltipFieldsComplete.simoleon_value, self.current_value) @property def base_value(self): return self._base_value @base_value.setter def base_value(self, value): self._base_value = round(max(value, 0)) update_tooltip = self.get_tooltip_field( TooltipFieldsComplete.simoleon_value) is not None self.update_current_value(update_tooltip=update_tooltip) @property def depreciated_value(self): if not self.definition.get_can_depreciate(): return self.catalog_value return self.catalog_value * (1 - self.INITIAL_DEPRECIATION) @property def catalog_value(self): return self.get_object_property(GameObjectProperty.CATALOG_PRICE) @property def depreciated(self): return not self._needs_depreciation def set_post_bb_fixup_needed(self): self._needs_post_bb_fixup = True self._needs_depreciation = True def try_post_bb_fixup(self, force_fixup=False, active_household_id=0): if force_fixup or self._needs_depreciation: if force_fixup: self._needs_depreciation = True self._on_try_depreciation(active_household_id=active_household_id) if force_fixup or self._needs_post_bb_fixup: self._needs_post_bb_fixup = False self.on_post_bb_fixup() @forward_to_components def on_post_bb_fixup(self): services.get_event_manager().process_events_for_household( test_events.TestEvent.ObjectAdd, services.household_manager().get(self._household_owner_id), obj=self) def _on_try_depreciation(self, active_household_id=0): if self._household_owner_id != active_household_id: return self._needs_depreciation = False if not self.definition.get_can_depreciate(): return self.base_value = floor(self._base_value * (1 - self.INITIAL_DEPRECIATION)) def register_for_on_children_changed_callback(self, callback): if self._on_children_changed is None: self._on_children_changed = CallableList() if callback not in self._on_children_changed: self._on_children_changed.append(callback) def unregister_for_on_children_changed_callback(self, callback): if self._on_children_changed is not None: if callback in self._on_children_changed: self._on_children_changed.remove(callback) if not self._on_children_changed: self._on_children_changed = None @distributor.fields.Field(op=distributor.ops.SetScratched, default=False) def scratched(self): return self._scratched @scratched.setter def scratched(self, scratched): self._scratched = scratched @distributor.fields.Field(op=distributor.ops.SetWindSpeedEffect, default=None) def wind_speed_level(self): return self._wind_speed_effect @wind_speed_level.setter def wind_speed_level(self, value): self._wind_speed_effect = value.wind_speed
from sims.sim_info_tracker import SimInfoTracker from singletons import DEFAULT from statistics.base_statistic import GalleryLoadBehavior from statistics.base_statistic_listener import BaseStatisticCallbackListener from traits.trait_type import TraitType import gsi_handlers import services import sims import sims4.callback_utils import sims4.log import uid logger = sims4.log.Logger('Statistic') lod_logger = sims4.log.Logger('LoD', default_owner='miking') with sims4.reload.protected(globals()): _handle_id_gen = uid.UniqueIdGenerator(1) class BaseStatisticTracker(SimInfoTracker): __slots__ = ('_statistics', '_owner', '_watchers', '_delta_watchers', '_listener_seeds', '_on_remove_callbacks', 'suppress_callback_setup_during_load', 'statistics_to_skip_load', 'suppress_callback_alarm_calculation', '_recovery_add_in_progress') def __init__(self, owner=None): self._statistics = None self._owner = owner self._watchers = {} self._delta_watchers = {} self._listener_seeds = {}
def __init__(self, situation): self._situation = situation self._has_offered_goals = False self._goal_id_generator = uid.UniqueIdGenerator(1)
class ClientObjectMixin: __qualname__ = 'ClientObjectMixin' INITIAL_DEPRECIATION = TunablePercent( 20, description= 'Amount (0%%-100%%) of depreciation to apply to an object after purchase. An item worth 10 in the catalog if tuned at 20%% will be worth 8 after purchase.' ) FADE_DURATION = TunableSimMinute( 1.2, description='Default fade time (in sim minutes) for objects.') VISIBLE_TO_AUTOMATION = True _get_next_ui_metadata_handle = uid.UniqueIdGenerator() FORWARD_OFFSET = 0.01 def __init__(self, definition, **kwargs): self._ui_metadata_stack = [] self._ui_metadata_handles = {} self._ui_metadata_cache = {} super().__init__(definition, **kwargs) if definition is not None: self.apply_definition(definition, **kwargs) self.primitives = distributor.ops.DistributionSet(self) self._location = sims4.math.Location( sims4.math.Transform(), routing.SurfaceIdentifier(sims4.zone_utils.get_zone_id(), 0, routing.SURFACETYPE_WORLD)) self._children = WeakSet() self._occupied_slot_dict = {} self._scale = 1 self._parent_type = ObjectParentType.PARENT_NONE self._parent_location = 0 self._build_buy_lockout = False self._build_buy_lockout_alarm_handler = None self._tint = None self._opacity = None self._censor_state = None self._geometry_state = None self._visibility = None self._material_state = None self._reference_arb = None self._audio_effects = {} self._video_playlist = None self._painting_state = None self._current_value = definition.price self._needs_post_bb_fixup = False self._needs_depreciation = False self._fade_out_alarm_handle = None def get_create_op(self, *args, **kwargs): additional_ops = list(self.get_additional_create_ops_gen()) return distributor.ops.ObjectCreate(self, additional_ops=additional_ops, *args, **kwargs) @forward_to_components_gen def get_additional_create_ops_gen(self): pass def get_create_after_objs(self): parent = self.parent_object if parent is not None: return (parent, ) return () def get_delete_op(self): return distributor.ops.ObjectDelete() @forward_to_components def apply_definition(self, definition, obj_state=0): if not isinstance(definition, objects.definition.Definition): definition = services.definition_manager().get(definition) self._model = definition.get_model(obj_state) self._material_variant = definition.material_variant self._rig = definition.get_rig(obj_state) self._slot = definition.get_slot(obj_state) self._slots_resource = definition.get_slots_resource(obj_state) self._state_index = obj_state def set_definition(self, definition_id, ignore_rig_footprint=False): new_definition = services.definition_manager().get(definition_id) (result, error) = self.definition.is_similar( new_definition, ignore_rig_footprint=ignore_rig_footprint) if not result: logger.error( 'Trying to set the definition {} to an incompatible definition {}.\n {}', self.definition.id, definition_id, error, owner='nbaker') return False services.definition_manager().unregister_definition( self.definition.id, self) self.apply_definition(new_definition, self._state_index) self.definition = new_definition services.definition_manager().register_definition( new_definition.id, self) self.resend_model_with_material_variant() self.resend_slot() self.resend_state_index() op = distributor.ops.SetObjectDefinitionId(definition_id) distributor.system.Distributor.instance().add_op(self, op) return True ui_metadata = distributor.sparse.SparseField( ui_protocols.UiObjectMetadata, distributor.ops.SetUiObjectMetadata) custom_name = ui_metadata.generic_property('custom_name', auto_reset=True) custom_description = ui_metadata.generic_property('custom_description', auto_reset=True) hover_tip = ui_metadata.generic_property('hover_tip') _generic_ui_metadata_setters = {} def add_ui_metadata(self, name, value, defer_update=False): if name not in self._ui_metadata_cache: default_value = type(self).ui_metadata.generic_getter(name)(self) self._ui_metadata_cache[name] = default_value handle = self._get_next_ui_metadata_handle() data = (handle, name, value) self._ui_metadata_stack.append(data) self._ui_metadata_handles[handle] = data if not defer_update: self.update_ui_metadata() return handle def remove_ui_metadata(self, handle, defer_update=False): self._ui_metadata_stack.remove(self._ui_metadata_handles[handle]) if not defer_update: self.update_ui_metadata() def update_ui_metadata(self, use_cache=True): ui_metadata = {} for (_, name, value) in self._ui_metadata_stack: ui_metadata[name] = value for (name, value) in ui_metadata.items(): if name in self._ui_metadata_cache and self._ui_metadata_cache[ name] == value and use_cache: pass if name in self._generic_ui_metadata_setters: setter = self._generic_ui_metadata_setters[name] else: setter = type(self).ui_metadata.generic_setter(name) self._generic_ui_metadata_setters[name] = setter setter(self, value) self._ui_metadata_cache = ui_metadata @distributor.fields.Field(op=distributor.ops.SetLocation, default=get_default_location(), direct_attribute_name='_location') def location(self): if self._location is not None: return self._location.duplicate() @location.setter def location(self, new_location): self.set_location_without_distribution(new_location) def set_location_without_distribution(self, new_location): if not isinstance(new_location, sims4.math.Location): raise TypeError() if new_location == self._location: return old_location = self._location events = [(self, old_location)] for child in self.children_recursive_gen(): events.append((child, child._location)) if new_location.parent != old_location.parent: self.pre_parent_change(new_location.parent) if old_location.parent is not None: old_location.parent._remove_child(self) if new_location.parent is not None: new_location.parent._add_child(self) visibility_state = self.visibility or VisibilityState() if new_location.parent is not None and new_location.parent._disable_child_footprint_and_shadow: visibility_state.enable_drop_shadow = False else: visibility_state.enable_drop_shadow = True self.visibility = visibility_state if new_location.parent is not None: current_inventory = self.get_inventory() if current_inventory is not None: if not current_inventory.try_remove_object_by_id(self.id): raise RuntimeError( 'Unable to remove object: {} from the inventory: {}, parenting request will be ignored.' .format(self, current_inventory)) posture_graph_service = services.current_zone().posture_graph_service with posture_graph_service.object_moving(self): self._location = new_location if new_location.parent != old_location.parent: self.on_parent_change(new_location.parent) for (obj, old_value) in events: obj.on_location_changed(old_value) def set_location(self, location): self.location = location def move_to(self, **overrides): self.location = self._location.clone(**overrides) @distributor.fields.Field(op=distributor.ops.SetAudioEffects) def audio_effects(self): return self._audio_effects @audio_effects.setter def audio_effects(self, value): self._audio_effects = value def append_audio_effect(self, key, value): self._audio_effects[key] = value self.audio_effects = self._audio_effects def remove_audio_effect(self, key): del self._audio_effects[key] self.audio_effects = self._audio_effects @forward_to_components def on_location_changed(self, old_location): pass @property def transform(self): return self._location.world_transform @transform.setter def transform(self, transform): if self.parent is not None: raise AssertionError( 'Cannot change the transform of a parented object directly. {} is parented to {}.' .format(self, self.parent)) self.move_to(transform=transform) @property def position(self): return self.transform.translation @property def position_with_forward_offset(self): return self.position + self.forward * ClientObjectMixin.FORWARD_OFFSET @property def intended_position_with_forward_offset(self): return self.intended_position + self.intended_forward * ClientObjectMixin.FORWARD_OFFSET @property def orientation(self): return self.transform.orientation @property def forward(self): return self.orientation.transform_vector( self.forward_direction_for_picking) @property def routing_surface(self): return self._location.world_routing_surface @property def routing_location(self): return self.get_routing_location_for_transform(self.transform) def get_routing_location_for_transform(self, transform): return routing.Location(transform.translation, transform.orientation, self.routing_surface) @property def intended_transform(self): return self.transform @property def intended_position(self): return self.intended_transform.translation @property def intended_forward(self): return self.intended_transform.orientation.transform_vector( self.forward_direction_for_picking) @property def intended_routing_surface(self): return self.routing_surface @property def parent(self): return self._location.parent @property def parent_object(self): parent = self.parent if parent is not None and parent.is_part: parent = parent.part_owner return parent @property def parent_slot(self): parent = self.parent if parent is None: return bone_name_hash = self._location.joint_name_or_hash or self._location.slot_hash result = None for runtime_slot in parent.get_runtime_slots_gen( bone_name_hash=bone_name_hash): if result is not None: raise AssertionError('Multiple slots!') result = runtime_slot if result is None: result = RuntimeSlot(parent, bone_name_hash, frozenset()) return result def get_parenting_root(self): result = self next_parent = result.parent while next_parent is not None: result = next_parent next_parent = result.parent return result @property def children(self): return self._children def children_recursive_gen(self, include_self=False): if include_self: yield self if self.is_part: obj_owner = self.part_owner else: obj_owner = self for child in obj_owner.children: yield child for grandchild in child.children_recursive_gen(): yield grandchild def parenting_hierarchy_gen(self): if self.parent is not None: master_parent = self.parent while master_parent.parent is not None: master_parent = master_parent.parent for child in master_parent.children_recursive_gen( include_self=True): yield child else: for child in self.children_recursive_gen(include_self=True): yield child def on_reset_send_op(self, reset_reason): super().on_reset_send_op(reset_reason) if self.valid_for_distribution and reset_reason != ResetReason.BEING_DESTROYED: try: reset_op = distributor.ops.ResetObject(self.id) dist = Distributor.instance() dist.add_op(self, reset_op) except: logger.exception('Exception thrown sending reset op for {}', self) def on_reset_internal_state(self, reset_reason): if self.valid_for_distribution: self.geometry_state = None self.material_state = None self._reset_reference_arb() super().on_reset_internal_state(reset_reason) def on_reset_get_interdependent_reset_records(self, reset_reason, reset_records): super().on_reset_get_interdependent_reset_records( reset_reason, reset_records) for child in self.children: reset_records.append( ResetRecord(child, ResetReason.RESET_EXPECTED, self, 'Child')) @property def slot_hash(self): return self._location.slot_hash @slot_hash.setter def slot_hash(self, value): if self._location.slot_hash != value: new_location = self._location new_location.slot_hash = value self.location = new_location @property def bone_name_hash(self): return self._location.joint_name_or_hash or self._location.slot_hash @property def part_suffix(self) -> str: pass @distributor.fields.Field(op=distributor.ops.SetModel) def model_with_material_variant(self): return (self._model, self._material_variant) resend_model_with_material_variant = model_with_material_variant.get_resend( ) @model_with_material_variant.setter def model_with_material_variant(self, value): (self._model, self._material_variant) = value @property def model(self): return self._model @model.setter def model(self, value): model_res_key = None if isinstance(value, sims4.resources.Key): model_res_key = value elif isinstance(value, Definition): model_res_key = value._model[0] self.set_definition(value.id, ignore_rig_footprint=True) else: if value is not None: logger.error( 'Trying to set the model of object {} to the invalid value of {}. The object will revert to its default model instead.', self, value, owner='tastle') model_res_key = self.definition.get_model(self._state_index) self.model_with_material_variant = (model_res_key, self._material_variant) @property def material_variant(self): return self._material_variant @material_variant.setter def material_variant(self, value): if value is None: self.model_with_material_variant = (self._model, None) else: if not isinstance(value, str): raise TypeError('Model variant value must be a string') if not value: self.model_with_material_variant = (self._model, None) else: variant_hash = sims4.hash_util.hash32(value) self.model_with_material_variant = (self._model, variant_hash) @distributor.fields.Field(op=distributor.ops.SetObjectDefStateIndex, default=0) def state_index(self): return self._state_index resend_state_index = state_index.get_resend() @distributor.fields.Field(op=distributor.ops.SetRig, priority=distributor.fields.Field.Priority.HIGH) def rig(self): return self._rig @rig.setter def rig(self, value): if not isinstance(value, sims4.resources.Key): raise TypeError self._rig = value @distributor.fields.Field(op=distributor.ops.SetSlot) def slot(self): return self._slot resend_slot = slot.get_resend() @property def slots_resource(self): return self._slots_resource @distributor.fields.Field(op=distributor.ops.SetScale, default=1) def scale(self): return self._scale @forward_to_components_gen def scale_modifiers_gen(self): pass @scale.setter def scale(self, value): for modifier in self.scale_modifiers_gen(): value *= modifier self._scale = value @property def parent_type(self): return self._parent_type @parent_type.setter def parent_type(self, value): self._parent_type = value self._resend_parent_type_info() @distributor.fields.Field(op=distributor.ops.SetParentType, default=None) def parent_type_info(self): return (self._parent_type, self._parent_location) @parent_type_info.setter def parent_type_info(self, value): (self._parent_type, self._parent_location) = value _resend_parent_type_info = parent_type_info.get_resend() @property def build_buy_lockout(self): return self._build_buy_lockout @distributor.fields.Field(op=distributor.ops.SetTint, default=None) def tint(self): if self.build_buy_lockout and lockout_visualization: return sims4.color.ColorARGB32(23782) return self._tint @tint.setter def tint(self, value): if value and not isinstance(value, sims4.color.ColorARGB32): raise TypeError('Tint value must be a Color') if value == sims4.color.Color.WHITE: self._tint = None else: self._tint = value resend_tint = tint.get_resend() @distributor.fields.Field(op=distributor.ops.SetOpacity, default=None) def opacity(self): return self._opacity @opacity.setter def opacity(self, value): self._opacity = self._clamp_opacity(value) def _clamp_opacity(self, value): if value is None: return try: value = float(value) except: raise TypeError('Opacity value must be a float') return sims4.math.clamp(0.0, value, 1.0) @distributor.fields.Field(op=distributor.ops.SetGeometryState, default=None) def geometry_state(self): return self._geometry_state @geometry_state.setter def geometry_state(self, value): if value is None: self._geometry_state = None else: if not isinstance(value, str): raise TypeError('Geometry State value must be a string') if len(value) == 0: self._geometry_state = None else: state_hash = sims4.hash_util.hash32(value) self._geometry_state = state_hash @distributor.fields.Field(op=distributor.ops.SetCensorState, default=None) def censor_state(self): return self._censor_state @censor_state.setter def censor_state(self, value): try: value = CensorState(value) except: raise TypeError('Censor State value must be an int') self._censor_state = value @distributor.fields.Field(op=distributor.ops.SetVisibility, default=None) def visibility(self): return self._visibility @visibility.setter def visibility(self, value): if not isinstance(value, VisibilityState): raise TypeError( 'Visibility must be set to value of type VisibilityState') self._visibility = value if value is not None and (value.visibility is True and value.inherits is False ) and value.enable_drop_shadow is False: self._visibility = None @distributor.fields.Field(op=distributor.ops.SetMaterialState, default=None) def material_state(self): return self._material_state @material_state.setter def material_state(self, value): if value is None: self._material_state = None else: if not isinstance(value, MaterialState): raise TypeError( 'Material State must be set to value of type MaterialState' ) if value.state_name_hash == 0: self._material_state = None else: self._material_state = value @property def material_hash(self): if self.material_state is None: return 0 return self.material_state.state_name_hash @distributor.fields.Field(op=distributor.ops.StartArb, default=None) def reference_arb(self): return self._reference_arb def update_reference_arb(self, arb): if self._reference_arb is None: self._reference_arb = animation.arb.Arb() native.animation.update_post_condition_arb(self._reference_arb, arb) def _reset_reference_arb(self): if self._reference_arb is not None: reset_arb_element = ArbElement(animation.arb.Arb()) reset_arb_element.add_object_to_reset(self) reset_arb_element.distribute() self._reference_arb = None _NO_SLOTS = EMPTY_SET @property def deco_slot_size(self): return get_object_decosize(self.definition.id) @property def deco_slot_types(self): return DecorativeSlotTuning.get_slot_types_for_object( self.deco_slot_size) @property def slot_type_set(self): key = get_object_slotset(self.definition.id) return get_slot_type_set_from_key(key) @property def slot_types(self): slot_type_set = self.slot_type_set if slot_type_set is not None: return slot_type_set.slot_types return self._NO_SLOTS @property def ideal_slot_types(self): carryable = self.get_component(CARRYABLE_COMPONENT) if carryable is not None: slot_type_set = carryable.ideal_slot_type_set if slot_type_set is not None: return slot_type_set.slot_types & (self.slot_types | self.deco_slot_types) return self._NO_SLOTS @property def all_valid_slot_types(self): return self.deco_slot_types | self.slot_types def _add_child(self, child): if not isinstance(self.children, (WeakSet, set)): raise TypeError( "self.children is not a WeakSet or a set, it's {}".format( self.children)) self.children.add(child) if self.parts: for part in self.parts: part.on_children_changed() self.on_child_added(child) def _remove_child(self, child): if not isinstance(self.children, (WeakSet, set)): raise TypeError( "self.children is not a WeakSet or a set, it's {}".format( self.children)) self.children.discard(child) if self.parts: for part in self.parts: part.on_children_changed() self.on_child_removed(child) def on_remove_from_client(self): super().on_remove_from_client() if self._fade_out_alarm_handle is not None: alarms.cancel_alarm(self._fade_out_alarm_handle) self._fade_out_alarm_handle = None for primitive in tuple(self.primitives): primitive.detach(self) def post_remove(self): for value in objects.components.component_attributes: while self.has_component(value): self.remove_component(value) for primitive in tuple(self.primitives): primitive.detach(self) self.primitives = None @forward_to_components def on_child_added(self, child): pass @forward_to_components def on_child_removed(self, child): pass @forward_to_components def pre_parent_change(self, parent): pass @forward_to_components def on_parent_change(self, parent): caches.clear_all_caches() if parent is None: self.parent_type = ObjectParentType.PARENT_NONE else: self.parent_type = ObjectParentType.PARENT_OBJECT def set_parent(self, parent, transform=sims4.math.Transform.IDENTITY(), joint_name_or_hash=None, slot_hash=0, routing_surface=None): part_joint_name = joint_name_or_hash or slot_hash if parent is not None and (part_joint_name is not None and not parent.is_part) and parent.parts: for part in parent.parts: while part.has_slot(part_joint_name): parent = part break import animation.animation_utils slot_name = animation.animation_utils.unhash_bone_name( part_joint_name) logger.error( 'Trying to parent({} to {} in slot {}) and there are no parts that contain the slot type.', self, parent, slot_name) new_location = self._location.clone( transform=transform, joint_name_or_hash=joint_name_or_hash, slot_hash=slot_hash, parent=parent, routing_surface=routing_surface) self.location = new_location def clear_parent(self, transform, routing_surface): return self.set_parent(None, transform=transform, routing_surface=routing_surface) def remove_reference_from_parent(self): if self.parent is not None: self.parent._remove_child(self) @distributor.fields.Field(op=distributor.ops.VideoSetPlaylistOp, default=None) def video_playlist(self): return self._video_playlist @video_playlist.setter def video_playlist(self, playlist): self._video_playlist = playlist _resend_video_playlist = video_playlist.get_resend() def fade_opacity(self, opacity, duration): opacity = self._clamp_opacity(opacity) if opacity != self._opacity: self._opacity = opacity fade_op = distributor.ops.FadeOpacity(opacity, duration) distributor.ops.record(self, fade_op) def fade_in(self): if self.visibility is not None and not self.visibility.visibility: self.visibility = VisibilityState() self.opacity = 0 self.fade_opacity(1, ClientObjectMixin.FADE_DURATION) def fade_out(self): self.fade_opacity(0, ClientObjectMixin.FADE_DURATION) def fade_and_destroy(self, **kwargs): def destroy(_): self.destroy(**kwargs) if self._fade_out_alarm_handle is not None: return self.fade_out() self._fade_out_alarm_handle = alarms.add_alarm( self, date_and_time.create_time_span( minutes=ClientObjectMixin.FADE_DURATION), destroy) @distributor.fields.Field(op=distributor.ops.SetValue, default=None) def current_value(self): return self._current_value @current_value.setter def current_value(self, value): self._current_value = value @property def depreciated_value(self): if not self.definition.get_can_depreciate(): return self.catalog_value return self.catalog_value * (1 - self.INITIAL_DEPRECIATION) @property def catalog_value(self): return self.get_object_property(GameObjectProperty.CATALOG_PRICE) @property def depreciated(self): return not self._needs_depreciation def set_post_bb_fixup_needed(self): self._needs_post_bb_fixup = True self._needs_depreciation = True def try_post_bb_fixup(self, force_fixup=False, active_household_id=0): if force_fixup or self._needs_depreciation: if force_fixup: self._needs_depreciation = True self._on_try_depreciation(active_household_id=active_household_id) if force_fixup or self._needs_post_bb_fixup: self._needs_post_bb_fixup = False self.on_post_bb_fixup() @forward_to_components def on_post_bb_fixup(self): services.get_event_manager().process_events_for_household( test_events.TestEvent.ObjectAdd, None, obj=self) def _on_try_depreciation(self, active_household_id=0): if self._household_owner_id != active_household_id: return self._needs_depreciation = False if not self.definition.get_can_depreciate(): return self.current_value = floor(self._current_value * (1 - self.INITIAL_DEPRECIATION))