def __init__(self, sim, source, priority, run_priority=None, client=None, pick=None, insert_strategy=QueueInsertStrategy.LAST, must_run_next=False, continuation_id=None, group_id=None, shift_held=False, carry_target=None, create_target_override=None, target_sim_id=None, bucket=InteractionBucketType.BASED_ON_SOURCE, visual_continuation_id=None, restored_from_load=False, cancel_if_incompatible_in_queue=False, always_check_in_use=False, source_interaction_id=None, source_interaction_sim_id=None, preferred_objects=(), preferred_carrying_sim=None, can_derail_if_constraint_invalid=True): self._sim = sim.ref() if sim else None self.source = source self.priority = priority self.client = client self.pick = pick self.insert_strategy = insert_strategy self.must_run_next = must_run_next self.shift_held = shift_held self.continuation_id = continuation_id self.visual_continuation_id = visual_continuation_id self.group_id = group_id self.source_interaction_id = source_interaction_id self.source_interaction_sim_id = source_interaction_sim_id self.carry_target = carry_target self.create_target_override = create_target_override self.target_sim_id = target_sim_id self.run_priority = run_priority self.bucket = bucket self.restored_from_load = restored_from_load self.cancel_if_incompatible_in_queue = cancel_if_incompatible_in_queue self.always_check_in_use = always_check_in_use self.preferred_objects = WeakSet(preferred_objects) self._preferred_carrying_sim = preferred_carrying_sim.ref() if preferred_carrying_sim is not None else None self.can_derail_if_constraint_invalid = can_derail_if_constraint_invalid
class Identity(object): """ Identities are abstract persons which persist throughout several sessions. They can be associated to several avatars. The `link` method adds an avatar to its available representation. Avatars are automatically removed when all references are lost due the reference's weakness. """ __metaclass__ = MultitonMeta def __init__(self, docid): self.docid = docid.encode('latin-1') self.avatars = WeakSet() def link(self, avatar): if avatar.identity not in (None, self): raise ValueError("double link from %r to %r and %r" % (avatar, self, avatar.identity)) service.dispatch_event("login", self, avatar) self.avatars.add(avatar) avatar.identity = self return self def fetch(self): return service.davenport.openDoc(self.docid)
def __init__(self, src: CodeFragment, begin: int = 0, end: int = 0, offset: int = 0) -> None: """ Generates a new code fragment-view backed by another code fragment. :param src: the assembly code fragment of which the new fragment will constitute a view :param begin: line number of the first line contained in the new fragment :param end: line number of the first line following the last contained in the new fragment :param offset: offset of the view inside the origin fragment :raises ValueError: when the fragment size would not fit inside the origin fragment """ # Delegate consistency checks super().__init__(src, begin, end, offset) self._origin = src self._begin = begin self._end = end self._offset = offset if src not in FragmentView._sources_catalogue: # If this source fragment has never been seen before, add it to the shared catalogue and allocate the views # catalogue self._views_catalogue = WeakSet() FragmentView._sources_catalogue[src] = self._views_catalogue else: # Otherwise, set the local reference to the views catalogue associated with the provided source self._views_catalogue = FragmentView._sources_catalogue[src] # Add the new view's metadata to the catalogue self._views_catalogue.add(self)
def add_reservation_handler(self, reservation_handler): if isinstance(self._reservation_handlers, tuple): self._reservation_handlers = WeakSet() self._reservation_handlers.add(reservation_handler) if self._on_reservation_handlers_changed: self._on_reservation_handlers_changed(user=reservation_handler.sim, added=True)
def __new__(mcls, name, bases, namespace, **kwargs): cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace, **kwargs) # Compute set of abstract method names abstracts = { name for name, value in namespace.items() if getattr(value, "__isabstractmethod__", False) } samplermixins = { name for name, value in namespace.items() if getattr(value, "__issamplemixin__", False) } if len(samplermixins) == 3: abstracts.update(samplermixins) for base in bases: for name in getattr(base, "__abstractmethods__", set()): value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) samplermixins = { name for name in getattr(base, "__abstractmethods__", set()) if getattr(getattr(cls, name, None), "__issamplemixin__", False) } if len(samplermixins) == 3: abstracts.update(samplermixins) cls.__abstractmethods__ = frozenset(abstracts) # Set up inheritance registry cls._abc_registry = WeakSet() cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls
def __init__(self, sim, target, reserver, all_parts=False): self._sim = sim self._target = target.ref() self._reserver = reserver self._registered = False self._all_parts = all_parts self._reserved_objects = WeakSet()
def __init__(self, *args, source_liability=None, **kwargs): super().__init__(**kwargs) self._released = False if source_liability is None: self._shared_liability_refs = WeakSet() else: self._shared_liability_refs = source_liability._shared_liability_refs self._shared_liability_refs.add(self)
def reevaluate_all_privacy_instances(): for privacy in services.privacy_service().privacy_instances: privacy._allowed_sims = WeakSet() privacy._disallowed_sims = WeakSet() privacy._violators = WeakSet() privacy._late_violators = WeakSet() for sim in TurboManagerUtil.Sim.get_all_sim_instance_gen( humans=True, pets=False): privacy.handle_late_violator(sim)
def __init__(self, parent, loggingLevel=logging.INFO): super(Subject, self).__init__() self._logger = logging.getLogger("[OBSERVER {} ({})]".format( parent.__class__.__name__.upper(), id(parent))) self._logger.setLevel(loggingLevel) self.parent = parent self._observers_lock = RLock() self._observers = WeakSet()
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 __init__(self, aop, context, **kwargs): super().__init__(aop, context, **kwargs) self.from_zone_id = kwargs['from_zone_id'] self.to_zone_id = kwargs['to_zone_id'] self.on_complete_callback = kwargs['on_complete_callback'] self.on_complete_context = kwargs['on_complete_context'] self._care_dependents = WeakSet() self._care_dependent_required = None if self.travel_care_dependents: self._find_care_dependents(context)
def _find_hamper_and_laundry_objects(self): object_manager = services.object_manager() self._laundry_hero_objects = WeakSet( object_manager.get_objects_with_tags_gen( *LaundryTuning.LAUNDRY_HERO_OBJECT_TAGS)) if self._affected_household is None: return self._hampers = WeakSet( object_manager.get_objects_with_tags_gen( *LaundryTuning.HAMPER_OBJECT_TAGS))
def __init__(self, type, source, destination, save_id=None): self.__type = type self.__data = type.ufl_type.build_default(None) self.__source = ref(source) self.__destination = ref(destination) self.__visuals = WeakSet() self.__cache = ModelTemporaryDataCache(None) if save_id is None: self.__save_id = uuid4() else: self.__save_id = save_id
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._enabled = True self._processing = False self._reset_in_progress = False self._last_work_timestamps = {} self._sims = set() self._active_work = {} self._denied_sims = OrderedDict() self._global_required_resources = WeakSet() self._gsi_entry = None self._gsi_log_entries = None
def __init__(self, name, poolSize=4, loggingLevel=logging.INFO): super(WorkerPool, self).__init__() self._logger = logging.getLogger("[WORKER POOL - {}]".format( name.upper())) self._logger.setLevel(loggingLevel) self._name = name self._isRunning = False self._tasksQueue = FIFOPriorityQueue() self._canceledTasks = WeakSet() self._workers = [PoolWorker(self) for _ in range(poolSize)]
class LightingLiability(Liability, HasTunableFactory, AutoFactoryInit): LIABILITY_TOKEN = 'LightingLiability' FACTORY_TUNABLES = {'radius_squared': TunableDistanceSquared(description='\n The distance away from the specified participant that lights will\n be turned off.\n ', default=1, display_name='Radius'), 'participant': TunableEnumEntry(description='\n The participant of the interaction that we will be used as the\n center of the radius to turn lights off.\n ', tunable_type=ParticipantType, default=ParticipantType.Actor)} def __init__(self, interaction, **kwargs): super().__init__(**kwargs) self._interaction = interaction self._lights = WeakSet() self._automated_lights = WeakSet() def on_run(self): if self._lights: return participant = self._interaction.get_participant(self.participant) position = participant.position for obj in services.object_manager().get_all_objects_with_component_gen(objects.components.types.LIGHTING_COMPONENT): if get_object_has_tag(obj.definition.id, LightingComponent.MANUAL_LIGHT_TAG): continue distance_from_pos = obj.position - position if distance_from_pos.magnitude_squared() > self.radius_squared: continue if obj.get_light_dimmer_value() == LightingComponent.LIGHT_AUTOMATION_DIMMER_VALUE: self._automated_lights.add(obj) else: self._lights.add(obj) obj.set_light_dimmer_value(LightingComponent.LIGHT_DIMMER_VALUE_OFF) def release(self): for obj in self._lights: obj.set_light_dimmer_value(LightingComponent.LIGHT_DIMMER_VALUE_MAX_INTENSITY) self._lights.clear() for obj in self._automated_lights: obj.set_light_dimmer_value(LightingComponent.LIGHT_AUTOMATION_DIMMER_VALUE) self._automated_lights.clear()
def __new__(mcls, name, bases, namespace): cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) # Compute set of abstract method names abstracts = { name for name, value in namespace.items() if getattr(value, "__isabstractmethod__", False) } # old ## for base in bases: ## for name in getattr(base, "__abstractmethods__", set()): ## value = getattr(cls, name, None) ## if getattr(value, "__isabstractmethod__", False): ## abstracts.add(name) # begin new names = set().union(*(getattr(base, "__abstractmethods__", set()) for base in bases)) names -= set(abstracts) if 0: d = ChainMap(*map(vars, cls.__mro__)) for name in names: # get decorator first; then attr value = d.get(name, None) if not getattr(value, "__isabstractmethod__", False): # get attr if hasattr(value, "__get__"): value = getattr(cls, name) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) else: for name in names: # get decorator first; then attr value = getattr_static(cls, name, None) if not getattr(value, "__isabstractmethod__", False): # get attr if hasattr(value, "__get__"): value = getattr(cls, name) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) # end new cls.__abstractmethods__ = frozenset(abstracts) # Set up inheritance registry cls._abc_registry = WeakSet() cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls
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 = []
def __new__(mcls, name, bases, namespace): cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) abstracts = set((name for name, value in namespace.items() if getattr(value, '__isabstractmethod__', False))) for base in bases: for name in getattr(base, '__abstractmethods__', set()): value = getattr(cls, name, None) if getattr(value, '__isabstractmethod__', False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) cls._abc_registry = WeakSet() cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls
def __init__(self, interaction, tests, max_line_of_sight_radius, map_divisions, simplification_ratio, boundary_epsilon, facing_offset): super().__init__(max_line_of_sight_radius, map_divisions, simplification_ratio, boundary_epsilon) self._max_line_of_sight_radius = max_line_of_sight_radius self._interaction = interaction self._tests = tests self._privacy_constraints = [] self._allowed_sims = WeakSet() self._disallowed_sims = WeakSet() self._violators = WeakSet() self._late_violators = WeakSet() self.is_active = False self.has_shooed = False self.central_object = None self._pushed_interactions = [] services.privacy_service().add_instance(self)
def children(self): if self._children_cache is None: self._children_cache = WeakSet() for child in self.part_owner.children: if self.has_slot(child.location.slot_hash or child.location.joint_name_hash): self._children_cache.add(child) elif self.part_owner.has_slot( child.location.slot_hash or child.location.joint_name_hash): if child.parts is not None: for part in child.parts: if part.attempt_to_remap_parent( part.location.parent) == self: self._children_cache.add(part) return self._children_cache
def _update_season_aware_objects_gen(self, timeline): for season_aware_object in WeakSet( services.object_manager().get_all_objects_with_component_gen( SEASON_AWARE_COMPONENT)): yield timeline.run_child(elements.SleepElement(TimeSpan.ZERO)) season_aware_object.season_aware_component.on_season_set( self.season)
def __subclasscheck__(cls, subclass): """Override for issubclass(subclass, cls).""" if subclass in cls._abc_cache: return True if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter elif subclass in cls._abc_negative_cache: return False ok = cls.__subclasshook__(subclass) if ok is not NotImplemented: if not isinstance(ok, bool): raise AssertionError if ok: cls._abc_cache.add(subclass) else: cls._abc_negative_cache.add(subclass) return ok cls in getattr(subclass, '__mro__', ()) and cls._abc_cache.add(subclass) return True for rcls in cls._abc_registry: if issubclass(subclass, rcls): cls._abc_cache.add(subclass) return True for scls in cls.__subclasses__(): if issubclass(subclass, scls): cls._abc_cache.add(subclass) return True cls._abc_negative_cache.add(subclass) return False
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 __subclasscheck__(cls, subclass): if subclass in cls._abc_cache: return True if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter elif subclass in cls._abc_negative_cache: return False ok = cls.__subclasshook__(subclass) if ok is not NotImplemented: if ok: cls._abc_cache.add(subclass) else: cls._abc_negative_cache.add(subclass) return ok if cls in getattr(subclass, '__mro__', ()): cls._abc_cache.add(subclass) return True for rcls in cls._abc_registry: if issubclass(subclass, rcls): cls._abc_cache.add(subclass) return True for scls in cls.__subclasses__(): if issubclass(subclass, scls): cls._abc_cache.add(subclass) return True cls._abc_negative_cache.add(subclass) return False
def __init__(self, inventory_type, item_location, max_size=None, allow_compaction=True, allow_ui=True, hidden_storage=False): self._objects = {} self._owners = WeakSet() self._inventory_type = inventory_type self._item_location = item_location self._max_size = max_size self._allow_compaction = allow_compaction self._allow_ui = allow_ui self._hidden_storage = hidden_storage self._stacks_with_options_counter = None
def __new__(mcls, name, bases, namespace): cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) # Compute set of abstract method names abstracts = set(name for name, value in list(namespace.items()) if getattr(value, "__isabstractmethod__", False)) for base in bases: for name in getattr(base, "__abstractmethods__", set()): value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) # Set up inheritance registry cls._abc_registry = WeakSet() cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls
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 children(self): if self._children_cache is None: self._children_cache = WeakSet({ child for child in self.part_owner.children if self.has_slot( child.location.slot_hash or child.location.joint_name_hash) }) return self._children_cache
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, 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 = services.current_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 self.objects_moved_via_live_drag = WeakSet()
class AttractorManagerMixin: ATTRACTOR_OBJECT_TAGS = TunableSet(description='\n One or more tags that indicate an object is a type of attractor point.\n We use attractor points to push Sims near things and reference specific\n geography in the world.\n ', tunable=TunableEnumWithFilter(description='\n A specific tag.\n ', tunable_type=tag.Tag, default=tag.Tag.INVALID, invalid_enums=(tag.Tag.INVALID,), filter_prefixes=('AtPo',))) SPAWN_POINT_ATTRACTORS = TunableMapping(description='\n Mapping from spawn point tags to attractor objects so we can create\n attractor points at spawn points.\n ', key_type=TunableEnumEntry(description='\n The tag on the spawn point.\n ', tunable_type=tag.Tag, default=tag.Tag.INVALID, invalid_enums=(tag.Tag.INVALID,)), key_name='spawn point tag', value_type=TunableReference(description='\n The object we want to create on the Spawn Point.\n ', manager=services.definition_manager(), pack_safe=True), value_name='attractor point definition') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._dynamic_attractor_ids = WeakSet() def create_dynamic_attractor_object(self, definition_id, location, tags_to_add=None): tags_to_add = set() if tags_to_add is None else tags_to_add def setup_obj(obj): obj.append_tags(tags_to_add) obj.location = location obj.persistence_group = PersistenceGroups.NONE created_obj = objects.system.create_object(definition_id, init=setup_obj) self._dynamic_attractor_ids.add(created_obj) if not self.ATTRACTOR_OBJECT_TAGS.intersection(created_obj.get_tags()): logger.warn('Attractor object does not have any tags in the ATTRACTOR OBJECT TAGS list. We need to be able to locate attractor objects and keep track of them.') return created_obj def destroy_dynamic_attractor_object(self, object_id): obj_to_destroy = self.get(object_id) if obj_to_destroy is None: logger.error('Object {} is not a dynamic attractor point.', object_id) return self._dynamic_attractor_ids.discard(obj_to_destroy) obj_to_destroy.destroy(obj_to_destroy, cause='Destroying Dynamic Attractor Point') def get_attractor_objects(self): return self.get_objects_matching_tags(AttractorManagerMixin.ATTRACTOR_OBJECT_TAGS) def create_spawn_point_attractor(self, spawn_point): obj_ids = set() for (spawn_point_tag, attractor_definition) in self.SPAWN_POINT_ATTRACTORS.items(): tags = spawn_point.get_tags() if spawn_point_tag in tags: location = sims4.math.Location(transform=spawn_point.get_approximate_transform(), routing_surface=spawn_point.routing_surface) obj = self.create_dynamic_attractor_object(attractor_definition, location, tags_to_add={spawn_point_tag}) obj_ids.add(obj.id) return frozenset(obj_ids)
class ABCMeta(type): """Metaclass for defining Abstract Base Classes (ABCs). Use this metaclass to create an ABC. An ABC can be subclassed directly, and then acts as a mix-in class. You can also register unrelated concrete classes (even built-in classes) and unrelated ABCs as 'virtual subclasses' -- these and their descendants will be considered subclasses of the registering ABC by the built-in issubclass() function, but the registering ABC won't show up in their MRO (Method Resolution Order) nor will method implementations defined by the registering ABC be callable (not even via super()). """ # A global counter that is incremented each time a class is # registered as a virtual subclass of anything. It forces the # negative cache to be cleared before its next use. # Note: this counter is private. Use `abc.get_cache_token()` for # external code. _abc_invalidation_counter = 0 def __new__(mcls, name, bases, namespace, /, **kwargs): cls = super().__new__(mcls, name, bases, namespace, **kwargs) # Compute set of abstract method names abstracts = { name for name, value in namespace.items() if getattr(value, "__isabstractmethod__", False) } for base in bases: for name in getattr(base, "__abstractmethods__", set()): value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) # Set up inheritance registry cls._abc_registry = WeakSet() cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls
def testWorkersDontStartRunningUntilToldTo(self): queue = FIFOPriorityQueue() canceledSet = WeakSet() poolWorker = PoolWorker(queue, canceledSet) self.assertFalse(poolWorker.isAlive()) poolWorker.start() self.assertTrue(poolWorker.isAlive()) queue.put((1, PoolWorker.STOP_TASK))
def __init__(self, sim, source, priority, run_priority=None, client=None, pick=None, insert_strategy=QueueInsertStrategy.LAST, must_run_next=False, continuation_id=None, group_id=None, shift_held=False, carry_target=None, target_sim_id=None, bucket=InteractionBucketType.BASED_ON_SOURCE, visual_continuation_id=None, restored_from_load=False, cancel_if_incompatible_in_queue=False, always_check_in_use=False, preferred_objects=()): self._sim = sim.ref() if sim else None self.source = source self.priority = priority self.client = client self.pick = pick self.insert_strategy = insert_strategy self.must_run_next = must_run_next self.shift_held = shift_held self.continuation_id = continuation_id self.visual_continuation_id = visual_continuation_id self.group_id = group_id self.carry_target = carry_target self.target_sim_id = target_sim_id self.run_priority = run_priority self.bucket = bucket self.restored_from_load = restored_from_load self.cancel_if_incompatible_in_queue = cancel_if_incompatible_in_queue self.always_check_in_use = always_check_in_use self.preferred_objects = WeakSet(preferred_objects)
def __init__(self, sim, target, track, animation_context=None): self._create_asm(animation_context=animation_context) self._source_interaction = None self._primitive = None self._owning_interactions = set() self._sim = None self._target = None self._target_part = None self._surface_target_ref = None self._track = None self._slot_constraint = UNSET self._context = None self._asm_registry = defaultdict(dict) self._asms_with_posture_info = set() self._failed_parts = set() self._bind(sim, target, track) self._linked_posture = None self._entry_anim_complete = False self._exit_anim_complete = False self.external_transition = False self._active_cancel_aops = WeakSet() self._saved_exit_clothing_change = None
def __init__(self, crystalManager=None, framesToGrowth=None, startCrystals=20, *args, **kwargs): GameElementBase.__init__(self, *args, **kwargs) if self.owner is not None: self.owner.addStructure(self) self._graph = nx.Graph() self._elementMapping = {} self._fixElements = WeakSet() if __debug__: print "FRAMESTO GROWTH", framesToGrowth self.framesToGrowth = framesToGrowth self.growTimer = 0 self._overDriveCounter = 0 self._overdrive = False self._veil = None self._growLock = False self._depletedCallback = None self._gameOverCallback = None self.crystalManager = crystalManager if crystalManager is None: logging.warn("Structure {!s} has no valid CrystalManager".format(self)) else: self.crystalManager.registerStructure(self) self._shadowWidth = util.CRYSTAL_SIZE * StructureElement.shapeOverflowFactor ** 2 if self.owner is None: self._shadowColor = "000000" else: self._shadowColor = self.owner.color player = avg.Player.get() self._canvas = player.createCanvas( id=str(id(self)), size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), handleevents=True, multisamplesamples=4, ) self._blackCanvas = player.createCanvas( id=str(id(self)) + "Black", size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), handleevents=True, multisamplesamples=4, ) self._canvasRoot = self._canvas.getRootNode() self._blackBackground = self._blackCanvas.getRootNode() self._shadowImage = avg.ImageNode( href="canvas:{}Black".format(id(self)), parent=self._root, size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), opacity=0.4, ) util.centerNodeOnPosition(self._shadowImage, (0, 0)) self._graphVisRoot = avg.DivNode(parent=self._canvasRoot) self._image = avg.ImageNode( href="canvas:{}".format(id(self)), parent=self._root, size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)) ) util.centerNodeOnPosition(self._image, (0, 0)) self._edgeNodes = dict() self._shadowNodes = dict() self._initStructureCore() assert self.checkSanity() while len(self._graph) < startCrystals: self.growSimple() self.rotationEnabled = True self._tickTimer = None self._startTickTimer()
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
class ReserveObjectHandler: __qualname__ = 'ReserveObjectHandler' LOCKOUT_TIME = TunableSimMinute(480, description='Number of sim minutes to lockout an in use object from autonomy.') def __init__(self, sim, target, reserver, all_parts=False): self._sim = sim self._target = target.ref() self._reserver = reserver self._registered = False self._all_parts = all_parts self._reserved_objects = WeakSet() @property def is_multi(self): return False def _is_valid_target(self, target): return True def get_targets(self): if self._target is not None: target = self._target() if not target.is_sim: if not self._all_parts: return (target,) if target.is_part: target = target.part_owner if target.parts: return target.parts return (target,) return () def _begin(self, element): if self.reserve(): return True return False def reserve(self): if self._registered: return True if self.may_reserve(): for target in self.get_targets(): target.reserve(self._sim, self._reserver, multi=self.is_multi) self._reserved_objects.add(target) self._registered = True return True return False def end(self, *_, **__): if self._registered: for target in self._reserved_objects: target.release(self._sim, self._reserver, multi=self.is_multi) self._registered = False def may_reserve(self, *args, **kwargs): targets = self.get_targets() for target in targets: test_result = self._is_valid_target(target) if not test_result: return test_result reserve_result = target.may_reserve(self._sim, multi=self.is_multi, *args, **kwargs) while not reserve_result: return reserve_result return TestResult.TRUE def do_reserve(self, sequence=None): return build_critical_section_with_finally(self._begin, sequence, self.end)
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()
def __init__(self, docid): self.docid = docid.encode('latin-1') self.avatars = WeakSet() self.service = service # closure
class BaseStructure(GameElementBase): neighbourhoodThreshold = 2 REACTION_THRESHOLD = util.REACTION_THRESHOLD class Veil(object): def __init__(self, structure, time, stopCallback=None, fadeOutTime=120.0, fadeInTime=120.0): self._structure = structure self._timeToVeil = time self._maxFadeOutTime = fadeOutTime self._fadeOutTime = fadeOutTime self._maxFadeInTime = fadeInTime self._fadeInTime = fadeInTime self._stopCallback = stopCallback self._action = self._veiling def onTick(self): if self._action is not None: self._action() def _veiling(self): if self._fadeInTime > 0: self._fadeInTime -= 1 self._setVeil(self._fadeInTime / self._maxFadeInTime) else: self._action = self._veilHolding def _veilHolding(self): if self._timeToVeil > 0: self._timeToVeil -= 1 self._setVeil(0) else: self._action = self._unveiling def _unveiling(self): if self._fadeOutTime > 0: self._fadeOutTime -= 1 self._setVeil(1 - (self._fadeOutTime / self._maxFadeOutTime)) else: self._action = self._finished def _finished(self): self._removeVeil() self._stopCallback() self._action = None def _setVeil(self, value): fxNode = avg.HueSatFXNode(0, 100 * value - 100, 100 * value - 100, False) self._structure._image.setEffect(fxNode) def _removeVeil(self): self._structure._image.setEffect(None) def __init__(self, crystalManager=None, framesToGrowth=None, startCrystals=20, *args, **kwargs): GameElementBase.__init__(self, *args, **kwargs) if self.owner is not None: self.owner.addStructure(self) self._graph = nx.Graph() self._elementMapping = {} self._fixElements = WeakSet() if __debug__: print "FRAMESTO GROWTH", framesToGrowth self.framesToGrowth = framesToGrowth self.growTimer = 0 self._overDriveCounter = 0 self._overdrive = False self._veil = None self._growLock = False self._depletedCallback = None self._gameOverCallback = None self.crystalManager = crystalManager if crystalManager is None: logging.warn("Structure {!s} has no valid CrystalManager".format(self)) else: self.crystalManager.registerStructure(self) self._shadowWidth = util.CRYSTAL_SIZE * StructureElement.shapeOverflowFactor ** 2 if self.owner is None: self._shadowColor = "000000" else: self._shadowColor = self.owner.color player = avg.Player.get() self._canvas = player.createCanvas( id=str(id(self)), size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), handleevents=True, multisamplesamples=4, ) self._blackCanvas = player.createCanvas( id=str(id(self)) + "Black", size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), handleevents=True, multisamplesamples=4, ) self._canvasRoot = self._canvas.getRootNode() self._blackBackground = self._blackCanvas.getRootNode() self._shadowImage = avg.ImageNode( href="canvas:{}Black".format(id(self)), parent=self._root, size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)), opacity=0.4, ) util.centerNodeOnPosition(self._shadowImage, (0, 0)) self._graphVisRoot = avg.DivNode(parent=self._canvasRoot) self._image = avg.ImageNode( href="canvas:{}".format(id(self)), parent=self._root, size=(max(util.WINDOW_SIZE), max(util.WINDOW_SIZE)) ) util.centerNodeOnPosition(self._image, (0, 0)) self._edgeNodes = dict() self._shadowNodes = dict() self._initStructureCore() assert self.checkSanity() while len(self._graph) < startCrystals: self.growSimple() self.rotationEnabled = True self._tickTimer = None self._startTickTimer() def getOffscreenPosForElement(self, pos): return util.vectorAdd(pos, util.vectorMult(self._canvasRoot.size, 0.5)) def getElementNodeParent(self): return self._canvasRoot def _startTickTimer(self): self._stopTickTimer() player = avg.Player.get() self._tickTimer = player.setOnFrameHandler(self.onTick) def _stopTickTimer(self): player = avg.Player.get() if self._tickTimer is not None: player.clearInterval(self._tickTimer) self._tickTimer = None def delete(self): if not self.alive: return self._stopTickTimer() if self.owner is not None: self.owner.removeStructure(self) for element in self._elementMapping.values(): if element.node is not None: element.node.unlink(True) element.node = None self._elementMapping = None for node in self._edgeNodes.values(): node.unlink(True) for node in self._shadowNodes.values(): node.unlink(True) self._edgeNodes = None self._shadowNodes = None self._rootParent = None self._canvasRoot.unlink(True) self._canvasRoot = None self._shadowImage.unlink(True) self._shadowImage = None self._graphVisRoot.unlink(True) self._graphVisRoot = None self._image.unlink(True) self._image = None player = avg.Player.get() player.deleteCanvas(self._blackCanvas.getID()) player.deleteCanvas(self._canvas.getID()) self._blackCanvas = None self._canvas = None self._fixElements = None self.crystalManager.removeStructure(self) self.crystalManager = None GameElementBase.delete(self) def _initStructureCore(self): raise NotImplementedError def _updateElement(self, element): self._elementMapping[element.shape] = element self._elementMapping[element.node] = element self._elementMapping[element.id] = element def getElement(self, obj): return self._elementMapping[obj] def startOverdrive(self, duration): self._overDriveCounter += duration def _overdriveEnd(self): pass @property def size(self): return self._size def _initPhysic(self, position, angle): mass = pymunk.inf moment = pymunk.moment_for_circle(mass, 0, 1) self._body = physic.BaseBody(self, mass, moment) self._space.add(self._body) def _createCircleShape(self, absPos, r=util.CRYSTAL_RADIUS): circle = pymunk.Circle(self._body, r, self._body.world_to_local(absPos)) circle.collision_type = physic.StructureCollisionType self._addShape(circle) return circle @property def depletedCallback(self): return self._depletedCallback @depletedCallback.setter def depletedCallback(self, fun): self._depletedCallback = fun def veil(self, time): if self._veil is None: self._veil = BaseStructure.Veil(self, time, self._veilEnd) def _veilEnd(self): self._veil = None def getAllElements(self): return [self.getElement(id) for id in self._graph] def onTick(self): if self._overDriveCounter > 0: self._overdrive = True self._overDriveCounter -= 1 if self._overDriveCounter == 0: self._overdrive = False self._overdriveEnd() self.grow() if self._veil is not None: self._veil.onTick() @property def gameOverCallback(self): return self._gameOverCallback @gameOverCallback.setter def gameOverCallback(self, fun): self._gameOverCallback = fun def onWallCollision(self, other): if self._gameOverCallback is not None: self._gameOverCallback() self.gameOverCallback = None def checkSanity(self): return True def updateNeigbourhoodVisualisation(self): if __debug__: if not hasattr(self, "debugNodes"): self.debugNodes = [] while len(self.debugNodes) < self._graph.number_of_edges(): debugNode = avg.LineNode(parent=self._root) debugNode.color = "FF0000" debugNode.strokewidth = 2 debugNode.opacity = 0.5 debugNode.fillopacity = 0.5 self.debugNodes.append(debugNode) while len(self.debugNodes) > self._graph.number_of_edges(): self.debugNodes.pop().unlink(True) for edge, node in zip(self._graph.edges_iter(), self.debugNodes): nodeIdA, nodeIdB = edge node.unlink(False) node.pos1 = tuple(self.getElement(nodeIdA).shape.offset) node.pos2 = tuple(self.getElement(nodeIdB).shape.offset) self._root.appendChild(node) def removeSurplusElements(self, element): assert self.checkSanity() removeCounter = 0 sameNeighbors = self.getSameExtendedNeighborhood(element) if self.REACTION_THRESHOLD > 0 and len(sameNeighbors) >= self.REACTION_THRESHOLD: for neighbor in sameNeighbors: self.removeElement(neighbor) removeCounter += 1 assert self.checkSanity() removeCounter += self.removeNotReachableElements() return removeCounter def getGraphNeighbors(self, element): return [self.getElement(id) for id in self._graph.neighbors_iter(element.id)] def addElement(self, elementToCreate, color, relPos, rotation=0): assert self.checkSanity() if color is None: element = elementToCreate(relPos, rotation, self, self._helpSystem) else: element = elementToCreate(color, relPos, rotation, self, self._helpSystem) self._updateElement(element) self._addElementShadow(element) self._graph.add_node(element.id) assert self.checkSanity() self.updateNeigbors(element) assert self.checkSanity() return element def _removeEdgeNodesForElement(self, element): neighbors = self.getGraphNeighbors(element) for edge in self._graph.edges_iter([element.id] + neighbors): if element.id in edge: self._removeEdgeNodes(edge) def _addEdgeNodes(self, edge): edge = tuple(sorted(edge)) elementA = self.getElement(edge[0]) elementB = self.getElement(edge[1]) shadowLine = avg.LineNode(parent=self._blackBackground) shadowLine.color = self._shadowColor shadowLine.strokewidth = self._shadowWidth shadowLine.opacity = 1 shadowLine.fillopacity = 1 shadowLine.pos1 = self.getOffscreenPosForElement(elementA.position) shadowLine.pos2 = self.getOffscreenPosForElement(elementB.position) self._shadowNodes[edge] = shadowLine avg.LinearAnim(shadowLine, "opacity", 700, 0, 1).start() edgeLine = avg.LineNode(parent=self._graphVisRoot) edgeLine.color = "F88017" edgeLine.strokewidth = util.CRYSTAL_SIZE * 0.1 edgeLine.opacity = 1 edgeLine.fillopacity = 1 edgeLine.pos1 = self.getOffscreenPosForElement(elementA.position) edgeLine.pos2 = self.getOffscreenPosForElement(elementB.position) avg.LinearAnim(edgeLine, "opacity", 5000, 0, 1).start() self._edgeNodes[edge] = edgeLine def _removeEdgeNodes(self, edge): edge = tuple(sorted(edge)) if edge in self._edgeNodes: edgeNode = self._edgeNodes[edge] avg.LinearAnim( edgeNode, "opacity", 100, edgeNode.opacity, 0, False, None, lambda: edgeNode.unlink(True) ).start() del self._edgeNodes[edge] if edge in self._shadowNodes: shadowNode = self._shadowNodes[edge] avg.LinearAnim( shadowNode, "opacity", 700, shadowNode.opacity, 0, False, None, lambda: shadowNode.unlink(True) ).start() self._shadowNodes[edge].unlink(True) del self._shadowNodes[edge] def _addElementShadow(self, element): roundShadow = avg.CircleNode( parent=self._blackBackground, pos=self.getOffscreenPosForElement(element.position), r=self._shadowWidth / 2, fillcolor=self._shadowColor, opacity=0, fillopacity=1, ) avg.LinearAnim(roundShadow, "fillopacity", 700, 0, 1).start() self._shadowNodes[element.shape] = roundShadow def _removeElementShadow(self, element): self._shadowNodes[element.shape].unlink(True) del self._shadowNodes[element.shape] def onCrystalCollision(self, other, hitShape): try: element = self.getElement(hitShape) except KeyError: return element.onCollision(other) def getColorCount(self): return collections.Counter([e.color for e in self._elementMapping.values()]) def removeElement(self, element): assert self.checkSanity() assert isinstance(element, StructureElement) assert element.parent == self element.onDestroyAction() self._removeEdgeNodesForElement(element) self._removeElementShadow(element) self._graph.remove_node(element.id) self._removeShape(element.shape) if element in self._fixElements: self._fixElements.remove(element) # avg.LinearAnim(element.node, "opacity",1000, 1 , 0, False, None, lambda:element.node.unlink(True)).start() # if self._fixElements: targetPos = random.choice(list(self._fixElements)).node.pos avg.LinearAnim( element.node, "pos", 1000, element.node.pos, targetPos, False, None, lambda: element.node.unlink(True) ).start() else: avg.LinearAnim(element.node, "opacity", 1000, 1, 0, False, None, lambda: element.node.unlink(True)).start() del self._elementMapping[element.shape] del self._elementMapping[element.node] del self._elementMapping[element.id] assert self.checkSanity() self._checkDepleted() if __debug__: self.updateNeigbourhoodVisualisation() def updateNeigbors(self, element, reset=False): assert self.checkSanity() assert isinstance(element, StructureElement) if reset: self._removeEdgeNodesForElement(element) self._graph.remove_node(element.id) self._graph.add_node(element.id) assert len(self._graph.neighbors(element.id)) == 0 for shape in self.getPhysicNeigbors(element): shapeId = self.getElement(shape).id if shapeId in self._graph: self._graph.add_edge(element.id, shapeId) self._addEdgeNodes((element.id, shapeId)) assert self.checkSanity() if __debug__: self.updateNeigbourhoodVisualisation() def getPhysicNeigbors(self, element): shape = element.shape testBody = pymunk.Body() testBody.position = self._body.local_to_world(shape.offset) toTest = pymunk.Circle(testBody, self.neighbourhoodThreshold * util.CRYSTAL_SIZE / 2) result = self._space.shape_query(toTest) result = filter(lambda s: s in self._shapes, result) return result def randomizeNeighbors(self, element): spatialInfo = [] for element in list(self.getGraphNeighbors(element)): spatialInfo.append((element.position, element.node.angle)) self.removeElement(element) for pos, angle in spatialInfo: color, toCreate = self.crystalManager.getNextStructureElement(self) self.addElement(toCreate, color, pos, angle) def _checkDepleted(self): if len(self._graph) == 0 and self.depletedCallback is not None: self._depletedCallback() self._depletedCallback = None def removeNeighbors(self, element): for element in list(self.getGraphNeighbors(element)): self.removeElement(element) def _swapShapes(self, elementA, elementB): assert self.checkSanity() if (elementA in self._fixElements) ^ (elementB in self._fixElements): if elementA in self._fixElements: self._fixElements.remove(elementA) self._fixElements.add(elementB) elif elementB in self._fixElements: self._fixElements.remove(elementB) self._fixElements.add(elementA) elementA.shape, elementB.shape = elementB.shape, elementA.shape self._updateElement(elementA) self._updateElement(elementB) assert self.checkSanity() def getSameExtendedNeighborhood(self, element): assert self.checkSanity() neighbourFilter = filter(lambda otherId: element.isSameType(self.getElement(otherId)), self._graph) subGraph = self._graph.subgraph(neighbourFilter) for graph in nx.connected_components(subGraph): if element.id in graph: assert self.checkSanity() return [self.getElement(i) for i in graph] else: assert self.checkSanity() return [] def removeNotReachableElements(self): toRemove = [] removeCounter = 0 for graph in nx.connected_component_subgraphs(self._graph): if all(element.id not in graph for element in self._fixElements): toRemove.append(graph) for graph in toRemove: for element in [self.getElement(i) for i in graph]: self.removeElement(element) removeCounter += 1 return removeCounter # def writeState(self): # assert(self.checkSanity()) # nodes = [] # for i, node in self._idNodeMapping.items(): # if node is not self: # nodes.append((i, # node.itemType, # node.pos.x, # node.pos.y, # node.shape.offset[0], # node.shape.offset[1])) # else: # nodes.append((0, # node.itemType, # node.pos.x, # node.pos.y, # 0,0)) # # edges= [] # for a,b in self._graph.edges_iter(): # if a == id(self): a=0 # if b == id(self): b=0 # edges.append((a,b)) # # with open("test", "w") as outFile: # pickle.dump((nodes,edges), outFile) # assert(self.checkSanity()) # # def loadState(self, filename): # assert(self.checkSanity()) # nodes, edges = pickle.load(filename) # # nodeMapping = dict() # # for i, crystalType, posX, posY, offsetX, offsetY in nodes: # if i == 0: # nodeMapping[i] = self # self._graph.add_node(id(self)) # else: # nodeMapping[i] = self.addElement(crystalType, # (posX,posY), # (offsetX, offsetY), # False) # self._idNodeMapping[id(nodeMapping[i])]= nodeMapping[i] # # for a,b in edges: # self._graph.add_edge(id(nodeMapping[a]), id(nodeMapping[b]) ) # assert(self.checkSanity()) # def grow(self): if self._growLock: return if self._overdrive: pass elif self.framesToGrowth is None: return elif self.growTimer < self.framesToGrowth: self.growTimer += 1 return else: self.growTimer = 0 newNode = self.growOutwards() # self.growSimple() def growOutwards(self): newSpot, elementChain = self.getRandomDestinationPath() if newSpot is None: return color, crystalType = self.getElementTypeToGrow() newElement = self.addElement(crystalType, color, newSpot) for element in reversed(elementChain[1:]): self._swapShapes(element, newElement) for element in elementChain[1:]: self.updateNeigbors(element, True) self.updateNeigbors(newElement, True) newElement.node.pos = elementChain[0].node.pos newPositions = itertools.chain( [element.node.pos for element in elementChain[1:]], (self.getOffscreenPosForElement(newSpot),) ) elementChain = itertools.chain((newElement,), elementChain[1:]) self._growLock = True for element, newPosition in zip(elementChain, newPositions): avg.LinearAnim(element.node, "pos", 1000, element.node.pos, newPosition).start() avg.LinearAnim(newElement.node, "opacity", 1000, 0, 1, False, None, self._resetGrowLock).start() assert self.checkSanity() def _resetGrowLock(self): self._growLock = False def getRandomDestinationPath(self): newSpot, targetElement = self.searchSpot() if targetElement is None: return None, None assert isinstance(targetElement, StructureElement) paths = [] for fixElement in self._fixElements: try: path = nx.shortest_path(self._graph, fixElement.id, targetElement.id) paths.append(path) except nx.exception.NetworkXNoPath: pass if not paths: return None, None shortestPath = min(paths, key=len) return newSpot, map(lambda x: self.getElement(x), shortestPath) def growSimple(self): newSpot, adjacentElement = self.searchSpot() if newSpot: color, crystalType = self.getElementTypeToGrow() element = self.addElement(crystalType, color, relPos=newSpot) return element def getElementTypeToGrow(self): return self.crystalManager.getNextStructureElement(self) def removeElementsInArea(self, pos, radius): deletedelements = 0 for shape in self._getShapesInCircle(pos, radius): if shape in self._elementMapping: deletedelements += self.getElement(shape).delete() return deletedelements def _getShapesInCircle(self, pos, radius): testBody = pymunk.Body() testBody.position = pos shapeToTest = pymunk.Circle(body=testBody, radius=radius) intersections = self._space.shape_query(shapeToTest) return intersections def searchSpot(self): if self._graph.nodes(): spots = [] if not self._fixElements: return None, None fixelement = random.choice(list(self._fixElements)) spots = self.checkForSpace(fixelement) spots = self._filterSpots(spots, fixelement) if spots: return random.choice(spots), fixelement node = random.choice(self._graph.nodes()) element = self.getElement(node) spots = self.checkForSpace(element) spots = self._filterSpots(spots, element) if spots: return random.choice(spots), element return None, None def _filterSpots(self, spots, origin): return spots def checkForSpace(self, element): radius = element.shape.radius + util.CRYSTAL_RADIUS + 1 availablePositions = [] stepsize = 360 // 4 maxNeigbors = 0 for alpha in range(0, 360, stepsize): alpha += random.randint(0, stepsize - 1) # alpha+= random.randrange(stepsize) angle = (2 * math.pi) * (alpha / 360.0) vector = util.getVectotInDirection(angle, radius) posToCheck = util.vectorAdd(self.toAbsPos(element.position), vector) testBody = pymunk.Body() testBody.position = posToCheck shapeToTestInner = pymunk.Circle(body=testBody, radius=util.CRYSTAL_SIZE / 2) shapeToTestOuter = pymunk.Circle( body=testBody, radius=(util.CRYSTAL_SIZE / 2) * self.neighbourhoodThreshold ) intersectionsInner = self._space.shape_query(shapeToTestInner) # dnode = avg.CircleNode(parent=self._root.getParent(), pos=posToCheck, r=util.CRYSTAL_SIZE/2,fillcolor="00FFFF", strokewidth=0) if len(intersectionsInner) == 0 or (len(intersectionsInner) == 1 and element.shape in intersectionsInner): intersectionsOuter = self._space.shape_query(shapeToTestOuter) neighborCount = len(intersectionsOuter) # dnode.fillopacity=0.5 if neighborCount > maxNeigbors: maxNeigbors = neighborCount availablePositions = [self.toRelPos(posToCheck)] elif neighborCount == maxNeigbors: availablePositions.append(self.toRelPos(posToCheck)) return availablePositions
class Posture(metaclass=TunedInstanceMetaclass, manager=services.posture_manager()): __qualname__ = 'Posture' ASM_SOURCE = '_asm_key' INSTANCE_TUNABLES = {'mobile': Tunable(bool, False, tuning_filter=FilterTag.EXPERT_MODE, description='If True, the Sim can route in this posture.'), 'unconstrained': Tunable(bool, False, description='If True, the Sim can stand anywhere in this posture.'), 'ownable': Tunable(bool, True, description="If True, This posture is ownable by interactions. Ex: A posture like carry_nothing should not be ownable, because it will cause strange cancelations that don't make sense."), 'social_geometry': TunableTuple(social_space=TunablePolygon(description="\n The special geometry override for socialization in this posture. This defines\n where the Sim's attention is focused and informs the social positioning system where\n each Sim should stand to look most natural when interacting with this Sim. \n Ex: we override the social geometry for a Sim who is bartending to be a wider cone \n and be in front of the bar instead of embedded within the bar. This encourages Sims \n to stand on the customer-side of the bar to socialize with this Sim instead of coming \n around the back."), focal_point=TunableVector3(sims4.math.Vector3.ZERO(), description='Focal point when socializing in this posture, relative to Sim'), tuning_filter=FilterTag.EXPERT_MODE, description='The special geometry for socialization in this posture.'), ASM_SOURCE: TunableResourceKey(None, [sims4.resources.Types.STATEMACHINE], tuning_group=GroupNames.ANIMATION, description='The posture ASM.', category='asm'), '_actor_param_name': Tunable(str, 'x', source_location=ASM_SOURCE, source_query=SourceQueries.ASMActorSim, tuning_group=GroupNames.ANIMATION, description="\n The name of the actor parameter in this posture's ASM. By default, this is x, and you should probably\n not change it."), '_target_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMActorAll, tuning_group=GroupNames.ANIMATION, description="\n The actor name for the target object of this posture. Leave empty for postures with no target. \n In the case of a posture that targets an object, it should be the name of the object actor in \n this posture's ASM. \n ."), '_enter_state_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n The name of the entry state for the posture in the ASM. \n All postures should have two public states, not including entry and exit.\n This should be the first of the two states.'), '_exit_state_name': Tunable(str, 'exit', source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n The name of the exit state in the ASM. By default, this is exit.'), '_state_name': Tunable(str, None, source_location=ASM_SOURCE, source_query=SourceQueries.ASMState, tuning_group=GroupNames.ANIMATION, description='\n The main state name for the looping posture pose in the ASM.\n All postures should have two public states, not including entry and exit.\n This should be the second of the two states.'), '_supported_postures': TunableList(TunableTuple(posture_type=TunableReference(services.posture_manager(), description='A supported posture.'), entry=Tunable(bool, True, description=''), exit=Tunable(bool, True, description=''), transition_cost=OptionalTunable(Tunable(float, 1, description="Cost of the transition to this posture then calculating the Sim's transition sequence.")), preconditions=TunableEnumFlags(PosturePreconditions, PosturePreconditions.NONE), description='A list of postures that this posture supports entrance from and exit to. Defaults to [stand]')), '_supports_carry': Tunable(description='\n Whether or not there should be a carry version of this posture in\n the posture graph.\n ', tunable_type=bool, default=True), 'censor_level': TunableEnumEntry(CensorState, None, tuning_filter=FilterTag.EXPERT_MODE, description="\n The type of censor grid that will be applied to any Sim in this posture. \n A censor grid obscures different parts of a Sim's body depending on what censor level it is set at. \n For example, the LHAND censor level will obscure a Sim's left hand. \n By default, postures have no censor level association, which means no censor grid will be applied to them \n and every part of their body will be visible when in this posture.\n "), 'outfit_change': TunableOutfitChange(description='\n Define what outfits the Sim is supposed to wear when entering or\n exiting this posture.\n '), 'cost': Tunable(float, 0, description='( >= 0 ) The distance a sim is willing to pay to avoid using this posture (higher number discourage using the posture)'), 'idle_animation': TunableAnimationReference(callback=None, tuning_group=GroupNames.ANIMATION, description='The animation for a Sim to play while in this posture and waiting for interaction behavior to start.'), 'jig': OptionalTunable(TunableReference(manager=services.definition_manager(), description='The jig to place while the Sim is in this posture.'), description='An optional Jig to place while the Sim is in this posture.'), 'allow_affinity': Tunable(bool, True, description="\n If True, Sims will prefer to use this posture if someone\n they're interacting with is using the posture.\n \n Ex: If you chat with a sitting sim, you will prefer to\n sit with them and chat.\n "), 'additional_put_down_distance': Tunable(description="\n An additional distance in front of the Sim to start searching for\n valid put down locations when in this posture.\n \n This tunable is only respected for the Sim's body posture.\n ", tunable_type=float, default=0.5), 'additional_interaction_jig_fgl_distance': Tunable(description='\n An additional distance (in meters) in front of the Sim to start \n searching when using FGL to place a Jig to run an interaction.', tunable_type=float, default=0)} DEFAULT_POSTURE = TunableReference(services.get_instance_manager(sims4.resources.Types.POSTURE), description="The default affordance to use as the supported posture if nothing is tuned in a Posture's 'Supported Postures'") IS_BODY_POSTURE = True def test(self): return True @classproperty def target_name(cls): return cls._target_name def __init__(self, sim, target, track, animation_context=None): self._create_asm(animation_context=animation_context) self._source_interaction = None self._primitive = None self._owning_interactions = set() self._sim = None self._target = None self._target_part = None self._surface_target_ref = None self._track = None self._slot_constraint = UNSET self._context = None self._asm_registry = defaultdict(dict) self._asms_with_posture_info = set() self._failed_parts = set() self._bind(sim, target, track) self._linked_posture = None self._entry_anim_complete = False self._exit_anim_complete = False self.external_transition = False self._active_cancel_aops = WeakSet() self._saved_exit_clothing_change = None @classproperty def name(cls): return cls._posture_name or cls.__name__ @property def posture_context(self): return self._context @property def animation_context(self): return self._animation_context @property def surface_target(self): return self.sim.posture_state.surface_target @property def source_interaction(self): return self._source_interaction @source_interaction.setter def source_interaction(self, value): if value is None: logger.error('Posture {} get a None source interaction set', self) return self._source_interaction = value @property def owning_interactions(self): return self._owning_interactions def last_owning_interaction(self, interaction): if interaction not in self.owning_interactions: return False for owning_interaction in self.owning_interactions: while owning_interaction is not interaction and not owning_interaction.is_finishing: return False return True def add_owning_interaction(self, interaction): self._owning_interactions.add(interaction) def remove_owning_interaction(self, interaction): self._owning_interactions.remove(interaction) def clear_owning_interactions(self): from interactions.base.interaction import OWNS_POSTURE_LIABILITY try: for interaction in list(self._owning_interactions): interaction.remove_liability((OWNS_POSTURE_LIABILITY, self.track)) finally: self._owning_interactions.clear() def add_cancel_aop(self, cancel_aop): self._active_cancel_aops.add(cancel_aop) def kill_cancel_aops(self): for interaction in self._active_cancel_aops: interaction.cancel(FinishingType.INTERACTION_QUEUE, cancel_reason_msg='PostureOwnership. This posture wasgoing to be canceled, but another interaction took ownership over the posture. Most likely the current posture was already valid for the new interaction.') def get_idle_behavior(self): if self.idle_animation is None: logger.error('{} has no idle animation tuning! This tuning is required for all body postures!', self) return if self.source_interaction is None: logger.error('Posture({}) on sim:{} has no source interaction.', self, self.sim, owner='Maxr', trigger_breakpoint=True) return if self.owning_interactions and not self.multi_sim: interaction = list(self.owning_interactions)[0] else: interaction = self.source_interaction idle = self.idle_animation(interaction) auto_exit = get_auto_exit((self.sim,), asm=idle.get_asm()) return build_critical_section(auto_exit, idle, flush_all_animations) def log_info(self, phase, msg=None): from sims.sim_log import log_posture log_posture(phase, self, msg=msg) def _create_asm(self, animation_context=None): self._animation_context = animation_context or AnimationContext() self._animation_context.add_posture_owner(self) self._asm = animation.asm.Asm(self._asm_key, self._animation_context) _provided_postures = PostureManifest().intern() _posture_name = None family_name = None @classproperty def posture_type(cls): return cls @classmethod def is_same_posture_or_family(cls, other_cls): if cls == other_cls: return True return cls.family_name is not None and cls.family_name == other_cls.family_name @classmethod def _tuning_loading_callback(cls): def delclassattr(name): if name in cls.__dict__: delattr(cls, name) delclassattr('_provided_postures') delclassattr('_posture_name') delclassattr('family_name') PostureTransitionData = namedtuple('PostureTransitionData', ('preconditions', 'transition_cost')) _posture_transitions = {} @staticmethod def _add_posture_transition(source_posture, dest_posture, transition_data): Posture._posture_transitions[(source_posture, dest_posture)] = transition_data @contextmanager def __reload_context__(oldobj, newobj): posture_transitions = dict(oldobj._posture_transitions) yield None oldobj._posture_transitions.update(posture_transitions) @classmethod def _tuning_loaded_callback(cls): for posture_data in cls._supported_postures: transition_data = cls.PostureTransitionData(posture_data.preconditions, posture_data.transition_cost) if posture_data.entry: cls._add_posture_transition(posture_data.posture_type, cls, transition_data) while posture_data.exit: cls._add_posture_transition(cls, posture_data.posture_type, transition_data) asm = animation.asm.Asm(cls._asm_key, get_throwaway_animation_context()) provided_postures = asm.provided_postures if not provided_postures: return specific_name = None family_name = None for entry in provided_postures: entry_specific_name = entry.specific if not entry_specific_name: raise ValueError('{} must provide a specific posture for all posture definition rows.'.format(asm.name)) if specific_name is None: specific_name = entry_specific_name elif entry_specific_name != specific_name: raise ValueError('{}: {} provides multiple specific postures: {}'.format(cls, asm.name, [specific_name, entry_specific_name])) entry_family_name = entry.family while entry_family_name: if family_name is None: family_name = entry_family_name elif entry_family_name != family_name: raise ValueError('{}: {} provides multiple family postures: {}'.format(cls, asm.name, [family_name, entry_family_name])) cls._provided_postures = provided_postures cls._posture_name = specific_name cls.family_name = family_name if cls.idle_animation is None: logger.error('{} has no idle_animation tuned. Every posture must have an idle animation suite!', cls) @flexmethod def get_provided_postures(cls, inst, surface_target=DEFAULT, concrete=False): if inst is None: return cls._provided_postures provided_postures = inst._provided_postures surface_target = inst._resolve_surface_target(surface_target) if surface_target is None or surface_target == MATCH_NONE: surface_restriction = MATCH_NONE elif surface_target == MATCH_ANY: surface_restriction = surface_target else: surface_restriction = surface_target if concrete else AnimationParticipant.SURFACE if surface_restriction is not None: filter_entry = PostureManifestEntry(MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, MATCH_ANY, surface_restriction, True) provided_postures = provided_postures.intersection_single(filter_entry) return provided_postures def _resolve_surface_target(self, surface_target): if surface_target is DEFAULT: return self.surface_target return surface_target def _bind(self, sim, target, track): if self.sim is sim and self.target is target and self.target_part is None or self.target_part is target and self._track == track: return if self.target is not None and track == PostureTrack.BODY: part_suffix = self.get_part_suffix() for asm in self._asms_with_posture_info: while not asm.remove_virtual_actor(self._target_name, self.target, suffix=part_suffix): logger.error('Failed to remove previously-bound virtual posture container {} from asm {} on posture {}.', self.target, asm, self) if sim is not None: self._sim = sim.ref() else: self._sim = None self._intersection = None self._asm_registry.clear() self._asms_with_posture_info.clear() if target is not None: if self._target_name is not None and target is not sim: (route_type, _) = target.route_target if self._target is not None and (self._target() is not None and self._target().parts is not None) and target in self._target().parts: self._target_part = target.ref() else: self._target_part = None self._target = target.ref() else: self._target = target.ref() else: self._target_part = None self._target = None if track is not None: self._track = track else: self._track = None self._slot_constraint = UNSET def rebind(self, target, animation_context=None): self._release_animation_context() self._create_asm(animation_context=animation_context) self._bind(self.sim, target, self.track) def reset(self): if self._saved_exit_clothing_change is not None: self.sim.sim_info.set_current_outfit(self._saved_exit_clothing_change) self._saved_exit_clothing_change = None self._entry_anim_complete = False self._exit_anim_complete = False self._release_animation_context() self._source_interaction = None def _release_animation_context(self): if self._animation_context is not None: self._animation_context.remove_posture_owner(self) self._animation_context = None def kickstart_gen(self, timeline, posture_state): if PostureTrack.is_carry(self.track): is_body = False self.asm.set_parameter('location', 'inventory') else: is_body = True self.source_interaction = self.sim.create_default_si() idle_arb = animation.arb.Arb() self.append_transition_to_arb(idle_arb, None) self.append_idle_to_arb(idle_arb) begin_element = self.get_begin(idle_arb, posture_state) yield element_utils.run_child(timeline, begin_element) if is_body: default_si = self.source_interaction yield default_si.prepare_gen(timeline) yield default_si.enter_si_gen(timeline) yield default_si.setup_gen(timeline) result = yield default_si.perform_gen(timeline) if not result: raise RuntimeError('Sim: {} failed to enter default si: {}'.format(self, default_si)) def get_asm(self, animation_context, asm_key, setup_asm_func, use_cache=True, cache_key=DEFAULT, interaction=None, posture_manifest_overrides=None, **kwargs): dict_key = animation_context if cache_key is DEFAULT else cache_key if use_cache: asm_dict = self._asm_registry[dict_key] asm = asm_dict.get(asm_key) if asm is None: asm = animation.asm.Asm(asm_key, context=animation_context, posture_manifest_overrides=posture_manifest_overrides) if interaction is not None: asm.on_state_changed_events.append(interaction.on_asm_state_changed) asm_dict[asm_key] = asm else: asm = animation.asm.Asm(asm_key, context=animation_context) if interaction is not None: asm.on_state_changed_events.append(interaction.on_asm_state_changed) if asm.current_state == 'exit': asm.set_current_state('entry') if not (setup_asm_func is not None and setup_asm_func(asm)): return return asm def remove_from_cache(self, cache_key): if cache_key in self._asm_registry: for asm in self._asm_registry[cache_key].values(): del asm._on_state_changed_events[:] del self._asm_registry[cache_key] def _create_primitive(self, animate_in, dest_state): return PosturePrimitive(self, animate_in, dest_state, self._context) def _on_reset(self): self._primitive = None def __str__(self): return '{0}:{1}'.format(self.name, self.id) def __repr__(self): return standard_repr(self, self.id, self.target) @property def sim(self): if self._sim is not None: return self._sim() @property def target(self): if self._target_part is not None: return self._target_part() if self._target is not None: return self._target() @property def target_part(self): if self._target_part is not None: return self._target_part() @property def track(self): return self._track @property def is_active_carry(self): return PostureTrack.is_carry(self.track) and self.target is not None def get_slot_offset_locked_params(self, anim_overrides=None): locked_params = self._locked_params if anim_overrides is not None: locked_params += anim_overrides.params locked_params += {'transitionPosture': 'stand'} return locked_params def build_slot_constraint(self, create_posture_state_spec_fn=None): if self.target is not None and PostureTrack.is_body(self.track): return interactions.constraints.RequiredSlot.create_slot_constraint(self, create_posture_state_spec_fn=create_posture_state_spec_fn) @property def slot_constraint_simple(self): if self._slot_constraint is UNSET: self._slot_constraint = self.build_slot_constraint(create_posture_state_spec_fn=lambda *_, **__: None) return self._slot_constraint @property def slot_constraint(self): if self._slot_constraint is UNSET: self._slot_constraint = self.build_slot_constraint() return self._slot_constraint @classproperty def multi_sim(cls): return False @property def is_puppet(self): return False @property def is_mirrored(self): if self.target is not None and self.target.is_part: return self.target.is_mirrored() or False return False @property def linked_posture(self): return self._linked_posture @linked_posture.setter def linked_posture(self, posture): self._linked_posture = posture @property def asm(self): return self._asm @property def _locked_params(self): anim_overrides_actor = self.sim.get_anim_overrides(self._actor_param_name) params = anim_overrides_actor.params if self.target is not None: anim_overrides_target = self.target.get_anim_overrides(self.target_name) if anim_overrides_target is not None: params += anim_overrides_target.params if self.target.is_part: part_suffix = self.target.part_suffix if part_suffix is not None: params += {'subroot': part_suffix} if self.is_mirrored is not None: params += {'isMirrored': self.is_mirrored} return params @property def locked_params(self): if self.slot_constraint is None or self.slot_constraint.locked_params is None: return self._locked_params return self._locked_params + self.slot_constraint.locked_params def _setup_asm_container_parameter(self, asm, target, actor_name, part_suffix, target_name=None): if asm in self._asms_with_posture_info: return True if target_name is None: target_name = self._target_name result = False if target is not None and target_name is not None: result = asm.add_potentially_virtual_actor(actor_name, self.sim, target_name, target, part_suffix, target_participant=AnimationParticipant.CONTAINER) if not self._setup_custom_posture_target_name(asm, target): logger.error('Failed to set custom posture target {}', target) result = False if result: self._asms_with_posture_info.add(asm) return result def _setup_custom_posture_target_name(self, asm, target): _custom_target_name = target.custom_posture_target_name if _custom_target_name in asm.actors: (_custom_target_actor, _) = asm.get_actor_and_suffix(_custom_target_name) if _custom_target_actor is None: return asm.set_actor(target.custom_posture_target_name, target, suffix=None, actor_participant=AnimationParticipant.CONTAINER) return True def _setup_asm_carry_parameter(self, asm, target): pass def get_part_suffix(self, target=DEFAULT): if target is DEFAULT: target = self.target if target is not None: return target.part_suffix def setup_asm_posture(self, asm, sim, target, locked_params=frozendict(), actor_param_name=DEFAULT): if actor_param_name is DEFAULT: actor_param_name = self._actor_param_name if asm is None: logger.error('Attempt to setup an asm whose value is None.') return False if sim is None: logger.error('Attempt to setup an asm {0} on a sim whose value is None.', asm) return False if not asm.set_actor(actor_param_name, sim, actor_participant=AnimationParticipant.ACTOR): logger.error('Failed to set actor sim: {0} on asm {1}', actor_param_name, asm) return False sim.set_mood_asm_parameter(asm, actor_param_name) sim.set_trait_asm_parameters(asm, actor_param_name) if target.is_part: is_mirrored = target.is_mirrored() if is_mirrored is not None: locked_params += {'isMirrored': is_mirrored} part_suffix = self.get_part_suffix() if not (target is not None and self._target_name is not None and self._setup_asm_container_parameter(asm, target, actor_param_name, part_suffix)): logger.error('Failed to set actor target: {0} on asm {1}', self._target_name, asm) return False if not PostureTrack.is_body(self.track): self._update_non_body_posture_asm() sim.on_posture_event.append(self._update_on_posture_event) if locked_params: virtual_actor_map = {self._target_name: self.target} asm.update_locked_params(locked_params, virtual_actor_map) self._setup_asm_carry_parameter(asm, target) return True def _update_on_posture_event(self, change, dest_state, track, old_value, new_value): if change == PostureEvent.POSTURE_CHANGED: if track != self.track: if new_value is not None: self._update_non_body_posture_asm() if new_value != self: self.sim.on_posture_event.remove(self._update_on_posture_event) elif new_value != self: self.sim.on_posture_event.remove(self._update_on_posture_event) def _update_non_body_posture_asm(self): if self.sim.posture.target is not None: (previous_target, previous_suffix) = self.asm.get_virtual_actor_and_suffix(self._actor_param_name, self.sim.posture._target_name) if previous_target is not None: self.asm.remove_virtual_actor(self.sim.posture._target_name, previous_target, previous_suffix) self.sim.posture.setup_asm_interaction(self.asm, self.sim, self.target, self._actor_param_name, self._target_name) def _setup_asm_interaction_add_posture_info(self, asm, sim, target, actor_name, target_name, carry_target, carry_target_name, surface_target=DEFAULT, carry_track=DEFAULT): def set_posture_param(posture_param_str, carry_param_str, carry_actor_name, surface_actor_name): if not asm.set_actor_parameter(actor_name, sim, 'posture', posture_param_str): if not asm.set_parameter('posture', posture_param_str): return False logger.warn('Backwards compatibility with old posture parameter required by {}', asm.name) if not asm.set_actor_parameter(actor_name, sim, PARAM_CARRY_STATE, carry_param_str): asm.set_parameter('carry', carry_param_str) asm.set_parameter('isMirrored', self.is_mirrored) if target_name == carry_actor_name and target is not None: set_carry_track_param_if_needed(asm, sim, target_name, target, carry_track=carry_track) if carry_actor_name is not None and carry_target_name == carry_actor_name and carry_target is not None: set_carry_track_param_if_needed(asm, sim, carry_target_name, carry_target, carry_track=carry_track) if surface_actor_name is not None: _surface_target = self._resolve_surface_target(surface_target) if _surface_target: asm.add_potentially_virtual_actor(actor_name, sim, surface_actor_name, _surface_target, target_participant=AnimationParticipant.SURFACE) else: return False return True def build_carry_str(carry_state): if carry_state[0]: if carry_state[1]: return 'both' return 'left' if carry_state[1]: return 'right' return 'none' def setup_asm_container_parameter(chosen_posture_type): container_name = chosen_posture_type.target_name if not container_name: return True part_suffix = self.get_part_suffix() if self._setup_asm_container_parameter(asm, self.target, actor_name, part_suffix, target_name=container_name): return True return False carry_state = sim.posture_state.get_carry_state() supported_postures = asm.get_supported_postures_for_actor(actor_name) if supported_postures is None: return True filtered_supported_postures = self.sim.filter_supported_postures(supported_postures) if surface_target is DEFAULT: surface_target = self._resolve_surface_target(surface_target) if surface_target is not None: surface_target_provided = MATCH_ANY else: surface_target_provided = MATCH_NONE elif surface_target is not None: surface_target_provided = MATCH_ANY else: surface_target_provided = MATCH_NONE provided_postures = self.get_provided_postures(surface_target=surface_target_provided) best_supported_posture = get_best_supported_posture(provided_postures, filtered_supported_postures, carry_state) if best_supported_posture is None: logger.debug('Failed to find supported posture for actor {} on {} for posture ({}) and carry ({}). Interaction info claims this should work.', actor_name, asm, self, carry_state) return False carry_param_str = build_carry_str(carry_state) carry_actor_name = best_supported_posture.carry_target surface_actor_name = best_supported_posture.surface_target if not isinstance(surface_actor_name, str): surface_actor_name = None param_str_specific = best_supported_posture.posture_param_value_specific if best_supported_posture.is_overlay: return True if param_str_specific and set_posture_param(param_str_specific, carry_param_str, carry_actor_name, surface_actor_name) and setup_asm_container_parameter(best_supported_posture.posture_type_specific): return True param_str_family = best_supported_posture.posture_param_value_family if best_supported_posture.is_overlay: return True if param_str_family and set_posture_param(param_str_family, carry_param_str, carry_actor_name, surface_actor_name) and setup_asm_container_parameter(best_supported_posture.posture_type_family): return True return False def setup_asm_interaction(self, asm, sim, target, actor_name, target_name, carry_target=None, carry_target_name=None, create_target_name=None, surface_target=DEFAULT, carry_track=DEFAULT, actor_participant=AnimationParticipant.ACTOR, invalid_expected=False): if target_name is not None and (target_name == self._target_name and (target is not None and self.target is not None)) and target.id != self.target.id: if not invalid_expected: logger.error('Animation targets a different object than its posture, but both use the same actor name for the object. This is impossible to resolve. Actor name: {}, posture target: {}, interaction target: {}', target_name, target, self.target) return False if not asm.set_actor(actor_name, sim, actor_participant=actor_participant): logger.error('Failed to set actor: {0} on asm {1}', actor_name, asm) return False if sim.asm_auto_exit.apply_carry_interaction_mask: asm._set_actor_trackmask_override(actor_name, 50000, 'Trackmask_CarryInteraction') if target is not None and target_name is not None: from sims.sim import Sim if isinstance(target, Sim): if not target.posture.setup_asm_interaction(asm, target, None, target_name, None, actor_participant=AnimationParticipant.TARGET): return False else: asm.add_potentially_virtual_actor(actor_name, sim, target_name, target, target_participant=AnimationParticipant.TARGET) anim_overrides = target.get_anim_overrides(target_name) if anim_overrides is not None and anim_overrides.params: virtual_actor_map = {self._target_name: self.target} asm.update_locked_params(anim_overrides.params, virtual_actor_map) if not self._setup_custom_posture_target_name(asm, target): logger.error('Unable to setup custom posture target name for {} on {}', target, asm) _carry_target_name = carry_target_name or create_target_name if carry_target is not None and _carry_target_name is not None: asm.add_potentially_virtual_actor(actor_name, sim, _carry_target_name, carry_target, target_participant=AnimationParticipant.CARRY_TARGET) if not self._setup_asm_interaction_add_posture_info(asm, sim, target, actor_name, target_name, carry_target, carry_target_name, surface_target, carry_track): return False return True def get_begin(self, animate_in, dest_state): if self._primitive is not None: raise RuntimeError('Posture Entry({}) called multiple times without a paired exit.'.format(self)) self._primitive = self._create_primitive(animate_in, dest_state) return self._primitive.next_stage() def begin(self, animate_in, dest_state, context): self._context = context def _do_begin(timeline): logger.debug('{} begin Posture: {}', self.sim, self) begin = self.get_begin(animate_in, dest_state) result = yield element_utils.run_child(timeline, begin) return result return _do_begin def get_end(self): if self._primitive is None: raise RuntimeError('Posture Exit({}) called multiple times without a paired entry. Sim: {}'.format(self, self.sim)) exit_behavior = self._primitive.next_stage() self._primitive = None return exit_behavior def end(self): def _do_end(timeline): logger.debug('{} end Posture: {}', self.sim, self) end = self.get_end() result = yield element_utils.run_child(timeline, end) return result return _do_end def add_transition_extras(self, sequence): return sequence def enumerate_goal_list_ids(self, goal_list): raise RuntimeError('[bhill] This function is believed to be dead code and is scheduled for pruning. If this exception has been raised, the code is not dead and this exception should be removed.') if goal_list is not None: for (index, goal) in enumerate(goal_list): goal.tag = index def get_locked_params(self, source_posture): if source_posture is None: return self._locked_params updates = {TRANSITION_POSTURE_PARAM_NAME: source_posture.name} if source_posture.target is None: return self._locked_params + updates if source_posture.target.is_part and self.target is not None and self.target.is_part: if self.target.is_mirrored(source_posture.target): direction = 'fromSimLeft' else: direction = 'fromSimRight' updates['direction'] = direction return self._locked_params + updates def append_transition_to_arb(self, arb, source_posture, locked_params=frozendict(), **kwargs): if not self._entry_anim_complete: locked_params += self.get_locked_params(source_posture) if source_posture is not None: locked_params += {TRANSITION_POSTURE_PARAM_NAME: source_posture.name} if not self.setup_asm_posture(self.asm, self.sim, self.target, locked_params=locked_params): logger.error('Failed to setup the asm for the posture {}', self) return self._setup_asm_target_for_transition(source_posture) self.asm.request(self._enter_state_name, arb) linked_posture = self.linked_posture if linked_posture is not None: locked_params = linked_posture.get_locked_params(source_posture) linked_posture.setup_asm_posture(linked_posture._asm, linked_posture.sim, linked_posture.target, locked_params=locked_params) if not self.multi_sim: linked_posture._asm.request(linked_posture._enter_state_name, arb) self._entry_anim_complete = True def append_idle_to_arb(self, arb): self.asm.request(self._state_name, arb) if self._linked_posture is not None: self._linked_posture.append_idle_to_arb(arb) def append_exit_to_arb(self, arb, dest_state, dest_posture, var_map, locked_params=frozendict()): if not self._exit_anim_complete: self._setup_asm_target_for_transition(dest_posture) locked_params += self.locked_params if dest_posture is not None: locked_params += {TRANSITION_POSTURE_PARAM_NAME: dest_posture.name} if locked_params: virtual_actor_map = {self._target_name: self.target} self.asm.update_locked_params(locked_params, virtual_actor_map) self.asm.request(self._exit_state_name, arb) self._exit_anim_complete = True def _setup_asm_target_for_transition(self, transition_posture): if transition_posture is not None and transition_posture._target_name != self._target_name and transition_posture._target_name in self.asm.actors: (previous_target, previous_suffix) = self.asm.get_virtual_actor_and_suffix(self._actor_param_name, transition_posture._target_name) if previous_target is not None: self.asm.remove_virtual_actor(transition_posture.target_name, previous_target, previous_suffix) if not transition_posture._setup_asm_container_parameter(self.asm, transition_posture.target, self._actor_param_name, transition_posture.get_part_suffix()): logger.error('Failed to setup target container {} on {} from transition posture {}', transition_posture._target_name, self, transition_posture) return False return True def post_route_clothing_change(self, interaction, do_spin=True, **kwargs): si_outfit_change = interaction.outfit_change if si_outfit_change is not None and si_outfit_change.posture_outfit_change_overrides is not None: overrides = si_outfit_change.posture_outfit_change_overrides.get(self.posture_type) if overrides is not None: entry_outfit = overrides.get_on_entry_outfit(interaction) if entry_outfit is not None: return overrides.get_on_entry_change(interaction, do_spin=do_spin, **kwargs) if self.outfit_change is not None: return self.outfit_change.get_on_entry_change(interaction, do_spin=do_spin, **kwargs) @property def saved_exit_clothing_change(self): return self._saved_exit_clothing_change def transfer_exit_clothing_change(self, clothing_change): self._saved_exit_clothing_change = clothing_change def prepare_exit_clothing_change(self, interaction): si_outfit_change = interaction.outfit_change if si_outfit_change is not None and si_outfit_change.posture_outfit_change_overrides is not None: overrides = si_outfit_change.posture_outfit_change_overrides.get(self.posture_type) if overrides is not None: exit_outfit = overrides.get_on_exit_outfit(interaction) if exit_outfit is not None: self._saved_exit_clothing_change = overrides.get_on_exit_outfit(interaction) return if self.outfit_change and self._saved_exit_clothing_change is None: self._saved_exit_clothing_change = self.outfit_change.get_on_exit_outfit(interaction) def exit_clothing_change(self, interaction, *, sim=DEFAULT, do_spin=True, **kwargs): if self._saved_exit_clothing_change is None or interaction is None: return if sim is DEFAULT: sim = interaction.sim sim_info = sim.sim_info return build_critical_section(sim_info.sim_outfits.get_change_outfit_element(self._saved_exit_clothing_change, do_spin=do_spin), flush_all_animations) def ensure_exit_clothing_change_application(self): if self.sim.posture_state.body is not self and self._saved_exit_clothing_change is not None: self.sim.sim_info.set_current_outfit(self._saved_exit_clothing_change) self._saved_exit_clothing_change = None @classmethod def supports_posture_type(cls, posture_type): return (cls, posture_type) in cls._posture_transitions or (posture_type, cls) in cls._posture_transitions @classmethod def is_valid_transition(cls, source_posture_type, destination_posture_type, targets_match): transition_data = cls._posture_transitions.get((source_posture_type, destination_posture_type)) if transition_data is None: return False if targets_match: return True preconditions = transition_data.preconditions if preconditions is not None and preconditions & PosturePreconditions.SAME_TARGET: return False return True @classmethod def get_transition_cost(cls, posture_type): transition_data = cls._posture_transitions.get((cls, posture_type)) if transition_data is None: transition_data = cls._posture_transitions.get((posture_type, cls)) if transition_data is not None: return transition_data.transition_cost @classmethod def is_valid_target(cls, sim, target, **kwargs): return True
class InteractionContext: __qualname__ = 'InteractionContext' SOURCE_PIE_MENU = InteractionSource.PIE_MENU SOURCE_AUTONOMY = InteractionSource.AUTONOMY SOURCE_BODY_CANCEL_AOP = InteractionSource.BODY_CANCEL_AOP SOURCE_CARRY_CANCEL_AOP = InteractionSource.CARRY_CANCEL_AOP SOURCE_SCRIPT = InteractionSource.SCRIPT SOURCE_UNIT_TEST = InteractionSource.UNIT_TEST SOURCE_SOCIAL_ADJUSTMENT = InteractionSource.SOCIAL_ADJUSTMENT SOURCE_QUICKTIME = InteractionSource.QUICKTIME SOURCE_GET_COMFORTABLE = InteractionSource.GET_COMFORTABLE SOURCE_SCRIPT_WITH_USER_INTENT = InteractionSource.SCRIPT_WITH_USER_INTENT SOURCE_POSTURE_GRAPH = InteractionSource.POSTURE_GRAPH def __init__(self, sim, source, priority, run_priority=None, client=None, pick=None, insert_strategy=QueueInsertStrategy.LAST, must_run_next=False, continuation_id=None, group_id=None, shift_held=False, carry_target=None, target_sim_id=None, bucket=InteractionBucketType.BASED_ON_SOURCE, visual_continuation_id=None, restored_from_load=False, cancel_if_incompatible_in_queue=False, always_check_in_use=False, preferred_objects=()): self._sim = sim.ref() if sim else None self.source = source self.priority = priority self.client = client self.pick = pick self.insert_strategy = insert_strategy self.must_run_next = must_run_next self.shift_held = shift_held self.continuation_id = continuation_id self.visual_continuation_id = visual_continuation_id self.group_id = group_id self.carry_target = carry_target self.target_sim_id = target_sim_id self.run_priority = run_priority self.bucket = bucket self.restored_from_load = restored_from_load self.cancel_if_incompatible_in_queue = cancel_if_incompatible_in_queue self.always_check_in_use = always_check_in_use self.preferred_objects = WeakSet(preferred_objects) def _clone(self, **overrides): result = copy.copy(self) for (name, value) in overrides.items(): if value is DEFAULT: pass getattr(result, name) setattr(result, name, value) return result @property def bucket_type(self): return self.bucket @property def is_cancel_aop(self): return self.source == InteractionSource.BODY_CANCEL_AOP or self.source == InteractionSource.CARRY_CANCEL_AOP def clone_for_user_directed_choice(self): return self._clone(source=InteractionContext.SOURCE_PIE_MENU, priority=self.client.interaction_priority, insert_strategy=QueueInsertStrategy.LAST, continuation_id=None, group_id=None) def clone_for_autonomous_choice(self): return self._clone(source=InteractionContext.SOURCE_AUTONOMY, priority=interactions.priority.Priority.Low, insert_strategy=QueueInsertStrategy.LAST, continuation_id=None, group_id=None) def clone_for_insert_next(self, preferred_objects=DEFAULT, **kwargs): if preferred_objects is DEFAULT: preferred_objects = self.preferred_objects return self._clone(insert_strategy=QueueInsertStrategy.NEXT, preferred_objects=preferred_objects, restored_from_load=False, **kwargs) def clone_for_continuation(self, continuation_of_si, insert_strategy=QueueInsertStrategy.NEXT, continuation_id=DEFAULT, group_id=DEFAULT, preferred_objects=DEFAULT, **kwargs): if not continuation_of_si.immediate: if continuation_id is DEFAULT: continuation_id = continuation_of_si.id group_id = continuation_of_si.group_id else: logger.error('clone_for_continuation: attempting to create a continuation of an immediate interaction, support for this is deprecated and will be removed soon: {}', continuation_of_si, owner='jpollak/tastle') if preferred_objects is DEFAULT: preferred_objects = self.preferred_objects return self._clone(insert_strategy=insert_strategy, continuation_id=continuation_id, group_id=group_id, preferred_objects=preferred_objects, restored_from_load=False, **kwargs) def clone_for_parameterized_autonomy(self, source_si, group_id=DEFAULT, continuation_id=DEFAULT, visual_continuation_id=DEFAULT, **kwargs): if group_id is DEFAULT: group_id = source_si.group_id if continuation_id is DEFAULT: continuation_id = source_si.id if visual_continuation_id is DEFAULT: visual_continuation_id = source_si.id return self._clone(insert_strategy=QueueInsertStrategy.FIRST, group_id=group_id, continuation_id=continuation_id, run_priority=None, visual_continuation_id=source_si.id, **kwargs) def clone_from_immediate_context(self, continuation_of_si, **kwargs): if not continuation_of_si.immediate: logger.error('clone_from_immediate_context: attempting to create a continuation of a non-immediate interaction.', owner='tastle/jpollak') return self._clone(group_id=continuation_of_si.group_id, **kwargs) def clone_for_sim(self, sim, **overrides): return self._clone(_sim=sim.ref(), **overrides) def clone_for_concurrent_context(self): return self._clone(insert_strategy=QueueInsertStrategy.FIRST) @property def sim(self): if self._sim: return self._sim() def add_preferred_object(self, cur_obj): self.preferred_objects.add(cur_obj) def add_preferred_objects(self, obj_list): pass @property def carry_target(self): if self._carry_target: return self._carry_target() @carry_target.setter def carry_target(self, value): self._carry_target = value.ref() if value else None def __repr__(self): return '{0}.{1}({2}, {3}, {4})'.format(self.__module__, self.__class__.__name__, repr(self.sim), self.source, repr(self.priority))
class Privacy(LineOfSight): __qualname__ = 'Privacy' _PRIVACY_FOOTPRINT_TYPE = 5 _PRIVACY_DISCOURAGEMENT_COST = routing.get_default_discouragement_cost() _SHOO_CONSTRAINT_RADIUS = Tunable(description='\n The radius of the constraint a Shooed Sim will attempt to route to.\n ', tunable_type=float, default=2.5) _UNAVAILABLE_TOOLTIP = TunableLocalizedStringFactory(description='\n Tooltip displayed when an object is not accessible due to being inside\n a privacy region.\n ') _EMBARRASSED_AFFORDANCE = TunableReference(description='\n The affordance a Sim will play when getting embarrassed by walking in\n on a privacy situation.\n ', manager=services.affordance_manager()) def __init__(self, interaction, tests, max_line_of_sight_radius, map_divisions, simplification_ratio, boundary_epsilon, facing_offset): super().__init__(max_line_of_sight_radius, map_divisions, simplification_ratio, boundary_epsilon) self._max_line_of_sight_radius = max_line_of_sight_radius self._interaction = interaction self._tests = tests self._privacy_constraints = [] self._allowed_sims = WeakSet() self._disallowed_sims = WeakSet() self._violators = WeakSet() self._late_violators = WeakSet() self.is_active = False self.has_shooed = False self.central_object = None self._pushed_interactions = [] services.privacy_service().add_instance(self) @property def unavailable_tooltip(self): return self._UNAVAILABLE_TOOLTIP @property def interaction(self): return self._interaction @property def is_active(self) -> bool: return self._is_active @is_active.setter def is_active(self, value): self._is_active = value def _is_sim_allowed(self, sim): if self._tests: resolver = self._interaction.get_resolver(target=sim) if self._tests and self._tests.run_tests(resolver): return True if self._interaction.can_sim_violate_privacy(sim): return True return False def evaluate_sim(self, sim): if self._is_sim_allowed(sim): self._allowed_sims.add(sim) return True self._disallowed_sims.add(sim) return False def build_privacy(self, target=None): self.is_active = True target_object = self._interaction.get_participant(ParticipantType.Object) target_object = None if target_object.is_sim else target_object self.central_object = target_object or (target or self._interaction.sim) self.generate(self.central_object.position, self.central_object.routing_surface) for poly in self.constraint.geometry.polygon: self._privacy_constraints.append(PolygonFootprint(poly, routing_surface=self._interaction.sim.routing_surface, cost=self._PRIVACY_DISCOURAGEMENT_COST, footprint_type=self._PRIVACY_FOOTPRINT_TYPE, enabled=True)) self._allowed_sims.update(self._interaction.get_participants(ParticipantType.AllSims)) for sim in services.sim_info_manager().instanced_sims_gen(): while sim not in self._allowed_sims: self.evaluate_sim(sim) violating_sims = self.find_violating_sims() self._cancel_unavailable_interactions(violating_sims) self._add_overrides_and_constraints_if_needed(violating_sims) def cleanup_privacy_instance(self): if self.is_active: self.is_active = False for sim in self._allowed_sims: self.remove_override_for_sim(sim) for sim in self._late_violators: self.remove_override_for_sim(sim) del self._privacy_constraints[:] self._allowed_sims.clear() self._disallowed_sims.clear() self._violators.clear() self._late_violators.clear() self._cancel_pushed_interactions() def remove_privacy(self): self.cleanup_privacy_instance() services.privacy_service().remove_instance(self) def intersects_with_object(self, obj): if obj.routing_surface != self.central_object.routing_surface: return False delta = obj.position - self.central_object.position distance = delta.magnitude_2d_squared() if distance > self.max_line_of_sight_radius*self.max_line_of_sight_radius: return False object_footprint = obj.footprint_polygon if object_footprint is None: object_footprint = sims4.geometry.Polygon([obj.position]) for poly in self.constraint.geometry.polygon: intersection = poly.intersect(object_footprint) while intersection is not None and intersection.has_enough_vertices: return True return False def find_violating_sims(self): if not self.is_active: return [] nearby_sims = placement.get_nearby_sims(self.central_object.position, self.central_object.routing_surface.secondary_id, radius=self.max_line_of_sight_radius, exclude=self._allowed_sims, only_sim_position=True) violators = [] for sim in nearby_sims: if any(sim_primitive.is_traversing_portal() for sim_primitive in sim.primitives if isinstance(sim_primitive, FollowPath)): pass if sim not in self._disallowed_sims and self.evaluate_sim(sim): pass while sims4.geometry.test_point_in_compound_polygon(sim.position, self.constraint.geometry.polygon): violators.append(sim) return violators def _add_overrides_and_constraints_if_needed(self, violating_sims): for sim in self._allowed_sims: self.add_override_for_sim(sim) for sim in violating_sims: self._violators.add(sim) liabilities = ((SHOO_LIABILITY, ShooLiability(self, sim)),) result = self._route_sim_away(sim, liabilities=liabilities) while result: self._pushed_interactions.append(result.interaction) def _cancel_unavailable_interactions(self, violating_sims): for sim in violating_sims: interactions_to_cancel = set() if sim.queue.running is not None: interactions_to_cancel.add(sim.queue.running) for interaction in sim.si_state: while interaction.is_super and interaction.target is not None and sim.locked_from_obj_by_privacy(interaction.target): interactions_to_cancel.add(interaction) for interaction in sim.queue: if interaction.target is not None and sim.locked_from_obj_by_privacy(interaction.target): interactions_to_cancel.add(interaction) else: while interaction.target is not None: break for interaction in interactions_to_cancel: interaction.cancel(FinishingType.INTERACTION_INCOMPATIBILITY, cancel_reason_msg='Canceled due to incompatibility with privacy instance.') def _route_sim_away(self, sim, liabilities=()): context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, Priority.High, insert_strategy=QueueInsertStrategy.NEXT) from interactions.utils.satisfy_constraint_interaction import BuildAndForceSatisfyShooConstraintInteraction result = sim.push_super_affordance(BuildAndForceSatisfyShooConstraintInteraction, None, context, liabilities=liabilities, privacy_inst=self, name_override='BuildShooFromPrivacy') if not result: logger.debug('Failed to push BuildAndForceSatisfyShooConstraintInteraction on Sim {} to route them out of a privacy area. Result: {}', sim, result, owner='tastle') self.interaction.cancel(FinishingType.TRANSITION_FAILURE, cancel_reason_msg='Failed to shoo Sims away.') return result def _cancel_pushed_interactions(self): for interaction in self._pushed_interactions: interaction.cancel(FinishingType.AUTO_EXIT, cancel_reason_msg='Privacy finished and is cleaning up.') self._pushed_interactions.clear() def handle_late_violator(self, sim): self._cancel_unavailable_interactions((sim,)) self.add_override_for_sim(sim) liabilities = ((LATE_SHOO_LIABILITY, LateShooLiability(self, sim)),) result = self._route_sim_away(sim, liabilities=liabilities) if not result: return if not self._violators: context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, Priority.High, insert_strategy=QueueInsertStrategy.NEXT) result = sim.push_super_affordance(self._EMBARRASSED_AFFORDANCE, self.interaction.get_participant(ParticipantType.Actor), context) if not result: logger.error('Failed to push the embarrassed affordance on Sim {}. Interaction {}. Result {}. Context {} ', sim, self.interaction, result, context, owner='tastle') return self._late_violators.add(sim) def add_override_for_sim(self, sim): for footprint in self._privacy_constraints: sim.routing_context.ignore_footprint_contour(footprint.footprint_id) def remove_override_for_sim(self, sim): for footprint in self._privacy_constraints: sim.routing_context.remove_footprint_contour_override(footprint.footprint_id) @property def allowed_sims(self): return self._allowed_sims @property def disallowed_sims(self): return self._disallowed_sims @property def violators(self): return self._violators def remove_violator(self, sim): self.remove_override_for_sim(sim) self._violators.discard(sim) @property def late_violators(self): return self._late_violators def remove_late_violator(self, sim): self.remove_override_for_sim(sim) self._late_violators.discard(sim)
def __init__(self, docid): self.docid = docid.encode('latin-1') self.avatars = WeakSet()