def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sim = self._context.sim self._valid_objects = [] for obj in services.object_manager().get_objects_matching_tags( self.object_tags, match_any=True): if self.placement_restriction is not None and self.placement_restriction == obj.is_outside: continue distance_from_sim = obj.position - self._sim.position if distance_from_sim.magnitude_squared( ) <= self.object_max_distance: if obj.is_connected(self._sim): self._valid_objects.append(obj) self._valid_objects = self.object_search_strategy.get_waypoint_objects( self._valid_objects) if not self._valid_objects: self._start_constraint = Circle( self._sim.position, self.constrain_radius, routing_surface=self._sim.routing_surface, los_reference_point=None) return if self.randomize_order: random.shuffle(self._valid_objects) starting_object = self._valid_objects.pop(0) self._start_constraint = Circle( starting_object.position, self.constrain_radius, routing_surface=self._sim.routing_surface, los_reference_point=None) self._start_constraint = self._start_constraint.intersect( self.get_water_constraint())
def get_start_constraint(self): if self._start_constraint is not None: return self._start_constraint sim = self._context.sim position = self._target.position if self._target is not None else sim.position relative_offset_vector = Vector3( 0, 0, self.ocean_constraint_distance_past_swim_portal) if self._target is not None and self._target.routing_surface is not None: routing_surface = self._target.routing_surface else: routing_surface = sim.routing_surface if routing_surface.type != routing.SurfaceType.SURFACETYPE_POOL: self._start_constraint = OceanStartLocationConstraint.create_simple_constraint( WaterDepthIntervals.SWIM, self.ocean_constraint_radius, sim, self._target, position, ideal_radius=self.constraint_width, ideal_radius_width=self.constraint_width, relative_offset_vector=relative_offset_vector) else: self._start_constraint = Circle( position, self.ocean_constraint_radius, routing_surface=self._routing_surface) self._master_depth_constraint = WaterDepthIntervalConstraint.create_water_depth_interval_constraint( sim, WaterDepthIntervals.SWIM) self._start_constraint = self._start_constraint.intersect( self._master_depth_constraint) return self._start_constraint
def _get_starting_constraint(self, *args, **kwargs): constraint = ANYWHERE target = self.target if self._waypoint_generator.is_for_vehicle and (target is not None and target.vehicle_component is not None) and not target.is_in_inventory(): constraint = Circle(target.position, target.vehicle_component.minimum_route_distance, routing_surface=target.routing_surface) constraint = constraint.intersect(self._waypoint_generator.get_water_constraint()) else: constraint = self._waypoint_generator.get_start_constraint() posture_constraint = self._waypoint_generator.get_posture_constraint() if posture_constraint is not None: constraint = constraint.intersect(posture_constraint) return constraint
def push_dismount_affordance(self, sim, final_location, depend_on_si=None): if sim.posture.is_vehicle: constraint = sim.posture_state.posture_constraint else: constraint = self._create_drive_posture_constraint( self.drive_affordance.provided_posture_type) radius = max( self.owner.routing_component.object_radius * self.object_radius_dismount_multiplier, self.ideal_route_radius) circle_constraint = None wading_interval = TunedInterval(0.1, 0.1) (min_water_depth, max_water_depth) = OceanTuning.make_depth_bounds_safe_for_surface( sim.routing_surface, wading_interval) if not (min_water_depth is None and max_water_depth is None): water_constraint = Constraint(min_water_depth=min_water_depth, max_water_depth=max_water_depth) if water_constraint.is_location_water_depth_valid(final_location): constraint = constraint.intersect(water_constraint) else: large_radius = max(self.minimum_route_distance, radius) circle_constraint = Circle( final_location.transform.translation, large_radius, sim.routing_surface, ideal_radius=self.ideal_route_radius, los_reference_point=DEFAULT) water_constraint = water_constraint.intersect( circle_constraint) if water_constraint.is_any_geometry_water_depth_valid(): constraint = constraint.intersect(water_constraint) else: constraint = constraint.intersect(circle_constraint) circle_constraint = None if circle_constraint is None: circle_constraint = Circle(final_location.transform.translation, radius, sim.routing_surface, ideal_radius=self.ideal_route_radius, los_reference_point=DEFAULT) constraint = constraint.intersect(circle_constraint) proxy_obj = services.terrain_service.TerrainService.create_surface_proxy_from_location( final_location) constraint = constraint.intersect(circle_constraint) return self._push_affordance( sim, interactions.utils.satisfy_constraint_interaction. SatisfyConstraintSuperInteraction, proxy_obj, depend_on_si=depend_on_si, constraint_to_satisfy=constraint, name_override='DismountVehicle')
def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): water_constraint = self.get_water_constraint() for _ in range(waypoint_count - 1): if not self._valid_objects: return obj = self._valid_objects.pop(0) next_constraint_circle = Circle( obj.position, self.constrain_radius, los_reference_point=None, routing_surface=obj.routing_surface) next_constraint_circle = next_constraint_circle.intersect( water_constraint) yield next_constraint_circle
def get_center_of_mass_constraint(self): if not self: logger.warn('No Sims in ensemble when trying to construct constraint.') return ANYWHERE (level, position) = self.calculate_level_and_center_of_mass() routing_surface = routing.SurfaceIdentifier(services.current_zone_id(), level, routing.SurfaceType.SURFACETYPE_WORLD) return Circle(position, sqrt(self.max_ensemble_radius), routing_surface)
def _get_waypoint_constraints_from_polygons(self, polygons, object_constraints, waypoint_count): object_constraints = dict(object_constraints) total_area = sum(p.area() for (p, _) in itertools.chain.from_iterable(polygons.values())) sim_location = self._sim.routing_location sim_routing_context = self._sim.get_routing_context() restriction = None if self.object_tag_generator is not None: restriction = self.object_tag_generator.placement_restriction final_constraints = [] for (block_id, block_data) in polygons.items(): block_object_constraints = object_constraints.pop(block_id, ()) if restriction is not None: if restriction and block_id == 0: continue else: for (polygon, routing_surface) in block_data: polygon_waypoint_count = math.ceil(waypoint_count*(polygon.area()/total_area)) for position in random_uniform_points_in_compound_polygon(polygon, num=int(polygon_waypoint_count)): if not routing.test_connectivity_pt_pt(sim_location, routing.Location(position, routing_surface=self._routing_surface), sim_routing_context, ignore_objects=self._sim): continue position_constraint = Circle(position, self.constraint_radius, routing_surface=routing_surface) for block_object_constraint in tuple(block_object_constraints): intersection = block_object_constraint.intersect(position_constraint) if intersection.valid: block_object_constraints.remove(block_object_constraint) final_constraints.append(position_constraint) final_constraints.extend(block_object_constraints) final_constraints.extend(itertools.chain.from_iterable(object_constraints.values())) return final_constraints
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self._target is None: self._start_constraint = Nowhere( 'Trying to generate a waypoint constraint without a target.') self._los_reference_point = None else: self._los_reference_point = self._target.position if self._target.is_terrain: self._los_reference_point = None self._start_constraint = Circle( self._target.position, self.object_constraint_radius, routing_surface=self._routing_surface, los_reference_point=self._los_reference_point) self._start_constraint = self._start_constraint.intersect( self.get_water_constraint())
class _WaypointGeneratorObjectPoints(_WaypointGeneratorBase): FACTORY_TUNABLES = { 'object_constraint_radius': TunableRange( description= '\n The radius, in meters, of the generated constraint around the \n target object where the waypoints will be generated.\n ', tunable_type=float, default=3, minimum=0), 'waypoint_constraint_radius': TunableRange( description= '\n The radius, in meters, for each generated waypoint inside the \n object constraint radius for the Sim to route to.\n ', tunable_type=float, default=1, minimum=1) } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self._target is None: self._start_constraint = Nowhere( 'Trying to generate a waypoint constraint without a target.') self._los_reference_point = None else: self._los_reference_point = self._target.position if self._target.is_terrain: self._los_reference_point = None self._start_constraint = Circle( self._target.position, self.object_constraint_radius, routing_surface=self._routing_surface, los_reference_point=self._los_reference_point) self._start_constraint = self._start_constraint.intersect( self.get_water_constraint()) def get_start_constraint(self): return self._start_constraint def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): polygon = self._start_constraint.geometry.polygon object_waypoint_constraints = [] object_waypoint = Circle(self._target.position, self.waypoint_constraint_radius, routing_surface=self._target.routing_surface, los_reference_point=self._los_reference_point) object_waypoint_constraints.append(object_waypoint) for position in random_uniform_points_in_compound_polygon( polygon, num=waypoint_count): object_waypoint_constraints.append( Circle(position, self.waypoint_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point)) object_waypoint_constraints.append(object_waypoint) object_waypoint_constraints = self.apply_water_constraint( object_waypoint_constraints) yield from object_waypoint_constraints
def _constraint_gen(cls, inst, sim, target, **kwargs): for constraint in super(SuperInteraction, cls)._constraint_gen(sim, target, **kwargs): yield constraint obj = cls.object_to_put_down(inst, sim=sim, target=target) yield create_carry_constraint(obj, debug_name='CarryForPutDown') yield Circle(sim.position, PUT_DOWN_GEOMETRY_RADIUS, routing_surface=sim.routing_surface)
def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): zone = services.current_zone() constraint_set = zone.get_spawn_points_constraint(except_lot_id=self._except_lot_id, sim_spawner_tags=self.spawn_point_tags, generalize=True) routing_context = routing_agent.routing_component.pathplan_context source_handle = routing.connectivity.Handle(routing_agent.position, routing_agent.routing_surface) dest_handles = set() for constraint in constraint_set: handles = constraint.get_connectivity_handles(routing_agent) dest_handles.update(handles) connectivity = routing.test_connectivity_batch((source_handle,), dest_handles, routing_context=routing_context, compute_cost=True) vehicle_dest_handles = {dest for (_, dest, cost) in connectivity if sims4.math.almost_equal(cost, 0.0)} constraint_set = create_constraint_set([handle.constraint for handle in vehicle_dest_handles]) constraints_weighted = [] min_score = sims4.math.MAX_FLOAT for constraint in constraint_set: spawn_point_vector = constraint.average_position - self._sim.position score = sims4.math.vector_dot_2d(self._pick_vector, spawn_point_vector) min_score = score constraints_weighted.append((score, constraint)) constraints_weighted = [(score - min_score, constraint) for (score, constraint) in constraints_weighted] constraints_weighted = sorted(constraints_weighted, key=lambda i: i[0]) first_constraint = constraints_weighted[-1][1] del constraints_weighted[-1] first_constraint_circle = Circle(first_constraint.average_position, self.constraint_radius, routing_surface=first_constraint.routing_surface) jog_waypoint_constraints = [] jog_waypoint_constraints.append(first_constraint_circle) last_waypoint_position = first_constraint.average_position for _ in range(waypoint_count - 1): constraints_weighted_next = [] for (_, constraint) in constraints_weighted: average_position = constraint.average_position distance_last = (average_position - last_waypoint_position).magnitude_2d() distance_home = (average_position - self._origin_position).magnitude_2d() constraints_weighted_next.append((distance_last + distance_home, constraint)) break next_constraint = pop_weighted(constraints_weighted_next) next_constraint_circle = Circle(next_constraint.average_position, self.constraint_radius, routing_surface=next_constraint.routing_surface) jog_waypoint_constraints.append(next_constraint_circle) constraints_weighted = constraints_weighted_next break last_waypoint_position = next_constraint.average_position jog_waypoint_constraints = self.apply_water_constraint(jog_waypoint_constraints) yield from jog_waypoint_constraints yield self._start_constraint
def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): polygon = self._start_constraint.geometry.polygon object_waypoint_constraints = [] object_waypoint = Circle(self._target.position, self.waypoint_constraint_radius, routing_surface=self._target.routing_surface, los_reference_point=self._los_reference_point) object_waypoint_constraints.append(object_waypoint) for position in random_uniform_points_in_compound_polygon( polygon, num=waypoint_count): object_waypoint_constraints.append( Circle(position, self.waypoint_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point)) object_waypoint_constraints.append(object_waypoint) object_waypoint_constraints = self.apply_water_constraint( object_waypoint_constraints) yield from object_waypoint_constraints
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self._target is None: self._start_constraint = Nowhere( 'No target for _WaypointGeneratorPacing') self._los_reference_point = None return self._los_reference_point = self._target.position if self._target.is_terrain: self._los_reference_point = None water_constraint = self.get_water_constraint( self.constraint_parameters.min_water_depth, self.constraint_parameters.max_water_depth) if self.outside_only: self._routing_surface = routing.SurfaceIdentifier( services.current_zone_id(), 0, routing.SurfaceType.SURFACETYPE_WORLD) starting_location = Location(position=self._target.position, routing_surface=self._routing_surface) search_flags = FGLSearchFlagsDefaultForSim | FGLSearchFlag.STAY_OUTSIDE fgl_context = placement.FindGoodLocationContext( starting_location, routing_context=self._context.sim.routing_context, additional_avoid_sim_radius=routing.get_default_agent_radius(), max_results=1, max_steps=10, search_flags=search_flags, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth()) (trans, _) = placement.find_good_location(fgl_context) if trans is not None: geometry = sims4.geometry.RestrictedPolygon( sims4.geometry.CompoundPolygon( sims4.geometry.Polygon((trans, ))), ()) self._start_constraint = SmallAreaConstraint( geometry=geometry, debug_name='WaypointPacingStartingConstraint', routing_surface=self._routing_surface, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth()) else: self._start_constraint = Nowhere( 'WaypointGeneratorPacing requires outside, but we failed to find a good location.' ) else: self._start_constraint = Circle( self._target.position, self.constraint_parameters.object_constraint_radius, routing_surface=self._routing_surface, los_reference_point=self._los_reference_point, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth())
def routing_debug_generate_routing_goals(x: float = None, y: float = None, z: float = None, radius: int = None, obj: OptionalTargetParam = None, _connection=None): if x is None or (y is None or z is None) or radius is None: sims4.commands.output('Please enter 4 floats for x,y,z and radius', _connection) return False obj = get_optional_target(obj, _connection=_connection) if obj is None: return False routing_component = obj.get_component(ROUTING_COMPONENT) if routing_component is None: return False if not postures.posture_graph.enable_debug_goals_visualization: sims4.commands.execute('debugvis.goals.enable', _connection) position = Vector3(x, y, z) routing_surface = routing.SurfaceIdentifier( services.current_zone_id(), 0, routing.SurfaceType.SURFACETYPE_WORLD) constraint = Circle(position, radius, routing_surface) handles = constraint.get_connectivity_handles(obj) handles_str = 'Handles: {}'.format(len(handles)) sims4.commands.output(handles_str, _connection) all_goals = [] for handle in handles: goal_list = handle.get_goals() goals_str = '\tGoals: {}'.format(len(goal_list)) sims4.commands.output(goals_str, _connection) all_goals.extend(goal_list) if postures.posture_graph.enable_debug_goals_visualization: with debugvis.Context('goal_scoring', routing_surface=routing_surface) as layer: for polygon in constraint.geometry.polygon: layer.add_polygon(polygon, routing_surface=routing_surface) for goal in all_goals: position = goal.location.transform.translation layer.add_point(position, routing_surface=routing_surface)
def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): if self._start_constraint is None: self.get_start_constraint() goals = [] handles = self._start_constraint.get_connectivity_handles( routing_agent) for handle in handles: goals.extend(handle.get_goals(always_reject_invalid_goals=True)) agent_radius = routing_agent.routing_component.pathplan_context.agent_radius ocean_goal_count = min(len(goals), self.ocean_unique_goal_count) for _ in range(ocean_goal_count): goal = random.choice(goals) break goals.remove(goal) constraint = Circle(goal.position, agent_radius, routing_surface=self._routing_surface) self._waypoint_constraints.append( constraint.intersect(self._master_depth_constraint)) available_waypoint_count = len(self._waypoint_constraints) if not self._start_constraint is not None or ( self._waypoint_constraints or not goals) or available_waypoint_count == 0: return use_pool_debug_visualizer = False and ( routing.waypoints.waypoint_generator.enable_waypoint_visualization and self._location_is_pool) polygon_metadata = {} for i in range(waypoint_count): if not not i % available_waypoint_count == 0 and self.shuffle_waypoints: random.shuffle(self._waypoint_constraints) yield self._waypoint_constraints[i % available_waypoint_count] if use_pool_debug_visualizer: self._build_polygon_metadata_dictionary( polygon_metadata, self._waypoint_constraints[i % available_waypoint_count], i) if not use_pool_debug_visualizer or use_pool_debug_visualizer: self._draw_pool_debugvis(polygon_metadata)
def put_down_geometry_constraint_gen(sim, target): if target.is_in_inventory(): yield Circle(sim.position, PUT_DOWN_GEOMETRY_RADIUS, routing_surface=sim.routing_surface) elif hasattr(target, 'get_carry_transition_constraint'): yield target.get_carry_transition_constraint(sim, target.position, target.routing_surface) else: logger.error( 'Trying to call get_carry_transition_constraint on Object {} that has no such attribute.\n Definition: {}\n Sim: {}\n ', target, target.definition, sim, owner='trevor')
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sim = self._context.sim if self._context.pick is not None: pick_position = self._context.pick.location self._pick_vector = pick_position - self._sim.position self._pick_vector /= self._pick_vector.magnitude() else: self._pick_vector = self._sim.forward if self._sim.is_on_active_lot(): plex_service = services.get_plex_service() if plex_service.is_active_zone_a_plex(): tags = (SpawnPoint.VISITOR_ARRIVAL_SPAWN_POINT_TAG,) else: tags = (SpawnPoint.ARRIVAL_SPAWN_POINT_TAG,) spawn_point = services.current_zone().get_spawn_point(lot_id=services.active_lot_id(), sim_spawner_tags=tags) self._origin_position = spawn_point.get_approximate_center() self._except_lot_id = services.active_lot_id() else: self._origin_position = self._sim.position self._except_lot_id = None self._routing_surface = routing.SurfaceIdentifier(services.current_zone_id(), 0, routing.SurfaceType.SURFACETYPE_WORLD) self._start_constraint = Circle(self._origin_position, self.constraint_radius, routing_surface=self._routing_surface, los_reference_point=None) self._start_constraint = self._start_constraint.intersect(self.get_water_constraint())
def create_put_down_in_self_inventory_constraint(inst, sim, target, cost=0): if cost is None: return Nowhere('No Cost({}). Sim: {} Target: {}', cost, sim, target) carry_constraint = create_carry_constraint( target, debug_name='CarryForPutDownInSimInventory') carry_constraint = carry_constraint.generate_constraint_with_cost(cost) constraint = sim.get_inventory_access_constraint(sim, True, target) constraint = constraint.apply_posture_state( None, inst.get_constraint_resolver(None)) posture_slot_constraint = sim.posture.slot_constraint if posture_slot_constraint: if not sim.parent_may_move: constraint = constraint.intersect(posture_slot_constraint) else: constraint = constraint.intersect( Circle(sim.position, PUT_DOWN_GEOMETRY_RADIUS, sim.routing_surface)) final_constraint = carry_constraint.intersect(constraint) return final_constraint
def get_routes_gen(self): waypoint_generator = self.waypoint_generator( WaypointContext(self._obj), None) waypoints = [] constraints = itertools.chain( (waypoint_generator.get_start_constraint(), ), waypoint_generator.get_waypoint_constraints_gen( self._obj, self.waypoint_count)) obj_start_constraint = Circle( self._obj.position, self.return_to_starting_point, routing_surface=self._obj.routing_surface, los_reference_point=None) constraints = itertools.chain(constraints, obj_start_constraint) for constraint in constraints: goals = list( itertools.chain.from_iterable( h.get_goals() for h in constraint.get_connectivity_handles(self._obj))) if not goals: continue for goal in goals: goal.orientation = sims4.math.angle_to_yaw_quaternion( random.uniform(0.0, sims4.math.TWO_PI)) waypoints.append(goals) if not (self.return_to_starting_point is not None and waypoints): return False yield routing_context = self._obj.get_routing_context() for route_waypoints in self.waypoint_stitching( waypoints, waypoint_generator.loops): route = routing.Route(self._obj.routing_location, route_waypoints[-1], waypoints=route_waypoints[:-1], routing_context=routing_context) yield route return True yield
def _get_close_to_deploy(self, timeline, vehicle): sim = self._interaction.sim constraint = self._get_deployment_constraint() if not constraint.valid: return InteractionQueuePreparationStatus.FAILURE yield handles = constraint.get_connectivity_handles(sim) goals = [] for handle in handles: goals.extend(handle.get_goals(single_goal_only=True)) if not goals: return InteractionQueuePreparationStatus.FAILURE yield if sim.posture.unconstrained: source_constraint = Position(sim.position, routing_surface=sim.routing_surface) else: source_constraint = Circle(sim.position, self.SOURCE_CONNECTIVITY_HANDLE_RADIUS, sim.routing_surface) source_handles = source_constraint.get_connectivity_handles(sim) if not source_handles: return InteractionQueuePreparationStatus.FAILURE yield source_goals = source_handles[0].get_goals(single_goal_only=True) if not source_goals: return InteractionQueuePreparationStatus.FAILURE yield source_goal = source_goals[0] if source_goal.position == goals[0].position and source_goal.routing_surface_id == goals[0].routing_surface_id: return InteractionQueuePreparationStatus.SUCCESS yield route = routing.Route(source_goal.location, goals, routing_context=sim.routing_context) plan_primitive = PlanRoute(route, sim, interaction=self._interaction) result = yield from element_utils.run_child(timeline, plan_primitive) if not result and not (not plan_primitive.path.nodes and not plan_primitive.path.nodes.plan_success): return InteractionQueuePreparationStatus.FAILURE yield cur_path = plan_primitive.path if not cur_path.nodes: return InteractionQueuePreparationStatus.FAILURE yield def get_start_node_index_for_path(vehicle, path): nodes = list(path.nodes) object_manager = services.object_manager() prev_node = None for node in nodes[::-1]: portal_obj_id = node.portal_object_id portal_obj = object_manager.get(portal_obj_id) if portal_obj_id else None if node.portal_id: if portal_obj: if not vehicle.vehicle_component.can_transition_through_portal(portal_obj, node.portal_id): break prev_node = node else: return 0 return prev_node.index split_paths = False while cur_path.next_path is not None: split_paths = True cur_path = cur_path.next_path if not cur_path.nodes: return InteractionQueuePreparationStatus.FAILURE yield start_node_index = get_start_node_index_for_path(vehicle, cur_path) start_node = cur_path.nodes[start_node_index] start_location = sims4.math.Location(Transform(Vector3(*start_node.position), Quaternion(*start_node.orientation)), start_node.routing_surface_id) if not split_paths and start_node_index == 0 and (start_location.transform.translation - source_goal.location.position).magnitude_squared() < vehicle.vehicle_component.minimum_route_distance: return InteractionQueuePreparationStatus.SUCCESS yield deploy_constraint = Position(start_location.transform.translation, routing_surface=start_location.routing_surface) depended_on_si = self._interaction affordance = self.get_close_affordance if self.get_close_affordance is not None else VehicleLiability.GET_CLOSE_AFFORDANCE aop = AffordanceObjectPair(affordance, None, affordance, None, route_fail_on_transition_fail=False, constraint_to_satisfy=deploy_constraint, allow_posture_changes=True, depended_on_si=depended_on_si) context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, Priority.High, insert_strategy=QueueInsertStrategy.FIRST, must_run_next=True, group_id=depended_on_si.group_id) if not aop.test_and_execute(context): return InteractionQueuePreparationStatus.FAILURE yield return InteractionQueuePreparationStatus.NEEDS_DERAIL yield
class _WaypointGeneratorSpawnPoints(_WaypointGeneratorBase): FACTORY_TUNABLES = {'constraint_radius': TunableRange(description='\n The radius, in meters, for each of the generated waypoint\n constraints.\n ', tunable_type=float, default=6, minimum=0), 'spawn_point_tags': OptionalTunable(description='\n Controls which spawn points can be used as waypoints.\n ', tunable=TunableSet(tunable=TunableEnumWithFilter(tunable_type=Tag, default=Tag.INVALID, filter_prefixes=SPAWN_PREFIX)))} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sim = self._context.sim if self._context.pick is not None: pick_position = self._context.pick.location self._pick_vector = pick_position - self._sim.position self._pick_vector /= self._pick_vector.magnitude() else: self._pick_vector = self._sim.forward if self._sim.is_on_active_lot(): plex_service = services.get_plex_service() if plex_service.is_active_zone_a_plex(): tags = (SpawnPoint.VISITOR_ARRIVAL_SPAWN_POINT_TAG,) else: tags = (SpawnPoint.ARRIVAL_SPAWN_POINT_TAG,) spawn_point = services.current_zone().get_spawn_point(lot_id=services.active_lot_id(), sim_spawner_tags=tags) self._origin_position = spawn_point.get_approximate_center() self._except_lot_id = services.active_lot_id() else: self._origin_position = self._sim.position self._except_lot_id = None self._routing_surface = routing.SurfaceIdentifier(services.current_zone_id(), 0, routing.SurfaceType.SURFACETYPE_WORLD) self._start_constraint = Circle(self._origin_position, self.constraint_radius, routing_surface=self._routing_surface, los_reference_point=None) self._start_constraint = self._start_constraint.intersect(self.get_water_constraint()) def get_start_constraint(self): return self._start_constraint def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): zone = services.current_zone() constraint_set = zone.get_spawn_points_constraint(except_lot_id=self._except_lot_id, sim_spawner_tags=self.spawn_point_tags, generalize=True) routing_context = routing_agent.routing_component.pathplan_context source_handle = routing.connectivity.Handle(routing_agent.position, routing_agent.routing_surface) dest_handles = set() for constraint in constraint_set: handles = constraint.get_connectivity_handles(routing_agent) dest_handles.update(handles) connectivity = routing.test_connectivity_batch((source_handle,), dest_handles, routing_context=routing_context, compute_cost=True) vehicle_dest_handles = {dest for (_, dest, cost) in connectivity if sims4.math.almost_equal(cost, 0.0)} constraint_set = create_constraint_set([handle.constraint for handle in vehicle_dest_handles]) constraints_weighted = [] min_score = sims4.math.MAX_FLOAT for constraint in constraint_set: spawn_point_vector = constraint.average_position - self._sim.position score = sims4.math.vector_dot_2d(self._pick_vector, spawn_point_vector) min_score = score constraints_weighted.append((score, constraint)) constraints_weighted = [(score - min_score, constraint) for (score, constraint) in constraints_weighted] constraints_weighted = sorted(constraints_weighted, key=lambda i: i[0]) first_constraint = constraints_weighted[-1][1] del constraints_weighted[-1] first_constraint_circle = Circle(first_constraint.average_position, self.constraint_radius, routing_surface=first_constraint.routing_surface) jog_waypoint_constraints = [] jog_waypoint_constraints.append(first_constraint_circle) last_waypoint_position = first_constraint.average_position for _ in range(waypoint_count - 1): constraints_weighted_next = [] for (_, constraint) in constraints_weighted: average_position = constraint.average_position distance_last = (average_position - last_waypoint_position).magnitude_2d() distance_home = (average_position - self._origin_position).magnitude_2d() constraints_weighted_next.append((distance_last + distance_home, constraint)) break next_constraint = pop_weighted(constraints_weighted_next) next_constraint_circle = Circle(next_constraint.average_position, self.constraint_radius, routing_surface=next_constraint.routing_surface) jog_waypoint_constraints.append(next_constraint_circle) constraints_weighted = constraints_weighted_next break last_waypoint_position = next_constraint.average_position jog_waypoint_constraints = self.apply_water_constraint(jog_waypoint_constraints) yield from jog_waypoint_constraints yield self._start_constraint
def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): water_constraint = self.get_water_constraint( self.constraint_parameters.min_water_depth, self.constraint_parameters.max_water_depth) debugvis_constraints = [] target_position = self._target.position object_radius_constraint = Circle( target_position, self.constraint_parameters.object_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth()) debugvis_constraints.append( (target_position, self.constraint_parameters.object_constraint_radius)) area_goals = [] handles = object_radius_constraint.get_connectivity_handles( routing_agent) for handle in handles: area_goals.extend( handle.get_goals(relative_object=self._target, always_reject_invalid_goals=True)) area_goals = [ goal for goal in area_goals if is_location_outside( goal.position, goal.location.routing_surface.secondary_id) ] if not (self.outside_only and area_goals): yield Circle( target_position, self.constraint_parameters.object_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth()) return min_dist_sq = self.waypoint_min_distance current_point = None for _ in range(waypoint_count): if current_point is None: current_point = random.choice(area_goals) debugvis_constraints.append( (current_point.position, self.constraint_parameters.waypoint_constraint_radius)) yield Circle( current_point.position, self.constraint_parameters.waypoint_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth()) farthest_point = None farthest_dist = 0 for _ in range(self.MAX_WAYPOINT_RANDOM_TRIES): try_point = random.choice(area_goals) try_dist = (try_point.position - current_point.position).magnitude_squared() farthest_point = try_point break if not (try_dist > min_dist_sq and (farthest_point is None or not farthest_point is not None)) and try_dist > farthest_dist: farthest_point = try_point farthest_dist = try_dist current_point = farthest_point debugvis_constraints.append( (current_point.position, self.constraint_parameters.waypoint_constraint_radius)) yield Circle( current_point.position, self.constraint_parameters.waypoint_constraint_radius, routing_surface=self._start_constraint.routing_surface, los_reference_point=self._los_reference_point, min_water_depth=water_constraint.get_min_water_depth(), max_water_depth=water_constraint.get_max_water_depth())
class _WaypointGeneratorMultipleObjectByTag(_WaypointGeneratorBase): FACTORY_TUNABLES = { 'object_max_distance': TunableDistanceSquared( description= '\n The maximum distance to check for an object as the next target\n of our waypoint interaction.\n ', default=5), 'constrain_radius': TunableRange( description= '\n The radius of the circle that will be generated around the objects\n where the waypoints will be generated.\n ', tunable_type=float, default=5, minimum=0), 'object_tags': TunableTags( description= '\n Find all of the objects based on these tags.\n ', filter_prefixes=('func', )), 'object_search_strategy': TunableVariant( description= '\n Search strategies to find and soft the possible objects where the\n waypoints will be generated.\n ', default_waypoints=_WaypointObjectDefaultStrategy.TunableFactory(), sorted_by_distance=_WaypointObjectSortedDistanceStrategy. TunableFactory(), default='default_waypoints'), 'placement_restriction': OptionalTunable( description= '\n If enabled the objects where the waypoints will be generated will\n be restricted to either the inside of outside.\n ', tunable=Tunable( description= '\n If checked objects will be restricted to the inside the \n house, otherwise only objects outside will be considered.\n ', tunable_type=bool, default=True), enabled_name='inside_only', disabled_name='no_restrictions'), 'randomize_order': Tunable( description= '\n If checked, the waypoints will be shuffled into a random order each\n time the route is generated. If not they will be the same (but\n still non-deterministic) order each time, for a given run.\n ', tunable_type=bool, default=False) } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sim = self._context.sim self._valid_objects = [] for obj in services.object_manager().get_objects_matching_tags( self.object_tags, match_any=True): if self.placement_restriction is not None and self.placement_restriction == obj.is_outside: continue distance_from_sim = obj.position - self._sim.position if distance_from_sim.magnitude_squared( ) <= self.object_max_distance: if obj.is_connected(self._sim): self._valid_objects.append(obj) self._valid_objects = self.object_search_strategy.get_waypoint_objects( self._valid_objects) if not self._valid_objects: self._start_constraint = Circle( self._sim.position, self.constrain_radius, routing_surface=self._sim.routing_surface, los_reference_point=None) return if self.randomize_order: random.shuffle(self._valid_objects) starting_object = self._valid_objects.pop(0) self._start_constraint = Circle( starting_object.position, self.constrain_radius, routing_surface=self._sim.routing_surface, los_reference_point=None) self._start_constraint = self._start_constraint.intersect( self.get_water_constraint()) def get_start_constraint(self): return self._start_constraint def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): water_constraint = self.get_water_constraint() for _ in range(waypoint_count - 1): if not self._valid_objects: return obj = self._valid_objects.pop(0) next_constraint_circle = Circle( obj.position, self.constrain_radius, los_reference_point=None, routing_surface=obj.routing_surface) next_constraint_circle = next_constraint_circle.intersect( water_constraint) yield next_constraint_circle
class _WaypointGeneratorPool(_WaypointGeneratorBase): FACTORY_TUNABLES = { 'constraint_width': TunableRange( description= '\n The width of the constraint created around the edge of the pool.\n ', tunable_type=float, default=1.5, minimum=0), 'ocean_constraint_radius': TunableRange( description= '\n When in the ocean, the radius of the area around the nearest swim\n portal to generate waypoints.\n ', tunable_type=float, default=30, minimum=0, maximum=1000), 'ocean_constraint_distance_past_swim_portal': TunableRange( description= '\n When in the ocean, an offset away from the nearest swim portal to\n center the area to generate waypoints.\n ', tunable_type=float, default=0, minimum=0), 'ocean_unique_goal_count': TunableRange( description= '\n When in the ocean, the number of unique waypoints to generate.\n ', tunable_type=int, default=10, minimum=0), 'shuffle_waypoints': Tunable( description= '\n If true, pool edge waypoint constraints will be shuffled and traversed in a random order.\n If false, pool edge waypoint constraints will be traversed in counter-clockwise order. \n ', tunable_type=bool, default=True), 'keep_away_from_edges': OptionalTunable( description= '\n If enabled, turns on a constraint that forces sims away from the pool edges by a tuned distance.\n ', tunable=Tunable( description= '\n The distance from the pool edge.\n ', tunable_type=float, default=0.25)) } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) sim = self._context.sim self._routing_surface = routing.SurfaceIdentifier( self._routing_surface.primary_id, self._routing_surface.secondary_id, routing.SurfaceType.SURFACETYPE_POOL) position = self._target.position if self._target is not None else sim.position level = self._routing_surface.secondary_id self._start_constraint = None self._master_depth_constraint = None self._waypoint_constraints = [] self.keep_away_constraint = None self._location_is_pool = build_buy.is_location_pool(position, level) if self._location_is_pool: pool_block_id = build_buy.get_block_id(sim.zone_id, position, level - 1) pool = pool_utils.get_pool_by_block_id(pool_block_id) if pool is not None: pool_edge_constraints = pool.get_edge_constraint( constraint_width=self.constraint_width, inward_dir=True, return_constraint_list=True) pool_edge_constraints = [ constraint.generate_geometry_only_constraint() for constraint in pool_edge_constraints ] if self.keep_away_from_edges is not None: bb_polys = build_buy.get_pool_polys( pool_block_id, level - 1) if len(bb_polys) > 0: bb_poly = bb_polys[0] _WaypointGeneratorPool._push_poly_inward( bb_poly, self.keep_away_from_edges) bb_poly.reverse() keep_away_geom = sims4.geometry.RestrictedPolygon( sims4.geometry.Polygon(bb_poly), ()) self.keep_away_constraint = Constraint( routing_surface=pool.provided_routing_surface, geometry=keep_away_geom) else: logger.error( f'Pool Waypoint Generator: Pool polygon data unexpectedly empty while ${sim} was routing on a pool with id ${pool_block_id}.', owner='jmorrow') for i in range(len(pool_edge_constraints)): pool_edge_constraints[i] = pool_edge_constraints[ i].intersect(self.keep_away_constraint) self._start_constraint = create_constraint_set( pool_edge_constraints) self._waypoint_constraints = pool_edge_constraints def get_start_constraint(self): if self._start_constraint is not None: return self._start_constraint sim = self._context.sim position = self._target.position if self._target is not None else sim.position relative_offset_vector = Vector3( 0, 0, self.ocean_constraint_distance_past_swim_portal) if self._target is not None and self._target.routing_surface is not None: routing_surface = self._target.routing_surface else: routing_surface = sim.routing_surface if routing_surface.type != routing.SurfaceType.SURFACETYPE_POOL: self._start_constraint = OceanStartLocationConstraint.create_simple_constraint( WaterDepthIntervals.SWIM, self.ocean_constraint_radius, sim, self._target, position, ideal_radius=self.constraint_width, ideal_radius_width=self.constraint_width, relative_offset_vector=relative_offset_vector) else: self._start_constraint = Circle( position, self.ocean_constraint_radius, routing_surface=self._routing_surface) self._master_depth_constraint = WaterDepthIntervalConstraint.create_water_depth_interval_constraint( sim, WaterDepthIntervals.SWIM) self._start_constraint = self._start_constraint.intersect( self._master_depth_constraint) return self._start_constraint def get_waypoint_constraints_gen(self, routing_agent, waypoint_count): if self._start_constraint is None: self.get_start_constraint() goals = [] handles = self._start_constraint.get_connectivity_handles( routing_agent) for handle in handles: goals.extend(handle.get_goals(always_reject_invalid_goals=True)) agent_radius = routing_agent.routing_component.pathplan_context.agent_radius ocean_goal_count = min(len(goals), self.ocean_unique_goal_count) for _ in range(ocean_goal_count): goal = random.choice(goals) break goals.remove(goal) constraint = Circle(goal.position, agent_radius, routing_surface=self._routing_surface) self._waypoint_constraints.append( constraint.intersect(self._master_depth_constraint)) available_waypoint_count = len(self._waypoint_constraints) if not self._start_constraint is not None or ( self._waypoint_constraints or not goals) or available_waypoint_count == 0: return use_pool_debug_visualizer = False and ( routing.waypoints.waypoint_generator.enable_waypoint_visualization and self._location_is_pool) polygon_metadata = {} for i in range(waypoint_count): if not not i % available_waypoint_count == 0 and self.shuffle_waypoints: random.shuffle(self._waypoint_constraints) yield self._waypoint_constraints[i % available_waypoint_count] if use_pool_debug_visualizer: self._build_polygon_metadata_dictionary( polygon_metadata, self._waypoint_constraints[i % available_waypoint_count], i) if not use_pool_debug_visualizer or use_pool_debug_visualizer: self._draw_pool_debugvis(polygon_metadata) def _draw_pool_debugvis(self, polygon_metadata): color_palette = [Color.WHITE, Color.BLUE, Color.GREEN, Color.MAGENTA] if routing.waypoints.waypoint_generator.enable_waypoint_visualization: with debugvis.Context(routing.waypoints.waypoint_generator. DEBUGVIS_WAYPOINT_LAYER_NAME) as layer: for entry in polygon_metadata.values(): position = entry[0] waypoint_indices = entry[1] layer.add_text_world(position, f'{waypoint_indices}') for (index, constraint) in enumerate(self._waypoint_constraints): polygon = constraint.geometry.polygon layer.add_polygon(polygon, color=color_palette[index % 4], altitude=0.1) if self.keep_away_from_edges is not None: polygon = self.keep_away_constraint.geometry.polygon layer.add_polygon(polygon, color=Color.BLACK, altitude=0.1) def _build_polygon_metadata_dictionary(self, polygon_metadata, constraint, waypoint_index): compound_polygon = constraint.geometry.polygon if isinstance(compound_polygon, CompoundPolygon): for polygon in compound_polygon: if len(polygon) > 0: key = polygon if key not in polygon_metadata: center = sum(polygon, Vector3.ZERO()) / len(polygon) polygon_metadata[key] = (center, []) waypoint_indices = polygon_metadata[key][1] waypoint_indices.append(waypoint_index) else: sim = self._context.sim logger.error( f'Pool Waypoint Generator: Polygon unexpectedly contains no vertices while drawing debug visuals of ${sim}"s route"', owner='jmorrow') else: sim = self._context.sim logger.error( f'Pool Waypoint Generator: Constraint geometry in unexpected format while drawing debug visuals of ${sim}"s route."', owner='jmorrow') @staticmethod def _push_poly_inward(verts, amt): for i in range(1, len(verts)): _WaypointGeneratorPool._push_edge_inward(verts, i - 1, i, amt) _WaypointGeneratorPool._push_edge_inward(verts, i, 0, amt) @staticmethod def _push_edge_inward(verts, start, stop, amt): along = amt * sims4.math.vector_normalize(verts[stop] - verts[start]) inward = sims4.math.vector3_rotate_axis_angle( along, sims4.math.PI / 2, sims4.math.Vector3.Y_AXIS()) verts[start] += inward verts[stop] += inward
def get_circle_constraint(cls, origin_position, radius, routing_surface, ideal_radius=None, ideal_radius_width=0): return Circle(origin_position, radius, routing_surface, ideal_radius=ideal_radius, ideal_radius_width=ideal_radius_width)