extends RigidBody2D class_name Unit enum TaskStatus { INTERRUPTED, DESTINATION_REACHED, ITEM_PICKED_UP, ITEM_DROPPED, } signal task_updated(task_status: TaskStatus) @export var movement_force: float = 100 @export var max_speed: float = 10 @export var path_give_up_theshold: float = 96.0 @export var stuck_speed: float = 5.0 @export var stuck_time: float = 1.0 @export var target_distance: float = 64.0 static var splat_scene: PackedScene = preload("res://objects/splat.tscn") var moving: bool = false var target: Vector2 = Vector2(1628, 1565): set(value): moving = true target = value find_path() var path: PackedVector2Array var path_ptr: int = 0 var stuck_timer: float = 0.0 var stuck: bool = false var facing: float = 0.1 func _process(delta: float) -> void: #$PathDebugLine.global_position = Vector2() if moving: if global_position.distance_squared_to(target) > target_distance**2: move(delta) else: moving = false send_task_update(TaskStatus.DESTINATION_REACHED) func move(delta: float) -> void: var direction: Vector2 if !stuck: var last_path_point: Vector2 = get_last_path_point() var path_point: Vector2 = get_next_path_point() # find out how far we are from the path var prev_dist_sqr: float = position.distance_squared_to(last_path_point) if !last_path_point.is_zero_approx() else 999999 var next_dist_sqr: float = position.distance_squared_to(path_point) # if we're too far from the path, give up on it and find a new one if next_dist_sqr > (path_give_up_theshold ** 2): find_path() path_point = get_next_path_point() # if we've reached the path point, move to the next one if next_dist_sqr < prev_dist_sqr: path_ptr += 1 path_point = get_next_path_point() #var direction: Vector2 = (Vector2(randf() - 0.5, randf() - 0.5) * 2) * 10000 direction = (path_point - position).normalized() else: direction = (Vector2(randf() - 0.5, randf() - 0.5) * 2) facing = direction.angle() var target_velocity: Vector2 = direction * max_speed var force: Vector2 = ((target_velocity - linear_velocity) / max_speed) * movement_force apply_force(force) #$ForceDebugLine.points[1] = force if linear_velocity.length() < stuck_speed: stuck_timer += delta if stuck_timer >= stuck_time: global_position = get_next_path_point() stuck_timer = 0 #set_stuck_mode(true) else: #if stuck: #set_stuck_mode(false) stuck_timer = 0 func get_last_path_point() -> Vector2: if path == null or path.is_empty(): find_path() if path_ptr >= path.size() and path.size() >= 2: return path[-2] if path.size() <= 1: return Vector2() if path != null and !path.is_empty(): return $"..".map_to_local(path[path_ptr - 1]) else: return Vector2() func get_next_path_point() -> Vector2: if path == null or path.is_empty(): find_path() if path_ptr >= path.size() and !path.is_empty(): return path[-1] if path != null and !path.is_empty(): return $"..".map_to_local(path[path_ptr]) else: return position func find_path() -> void: path_ptr = 0 var astar_grid: AStarGrid2D = $"..".astar_grid if astar_grid == null: return var source_coords: Vector2i = $"..".local_to_map(position) var target_coords: Vector2i = $"..".local_to_map(target) path = astar_grid.get_point_path(source_coords, target_coords, true) var debug_points: PackedVector2Array #debug_points.resize(path.size()) for point in path: #var point: Vector2 = path[idx] var global_point: Vector2 = $"..".map_to_local(point) debug_points.append(global_point) #$PathDebugLine.points = debug_points func set_stuck_mode(stk: bool) -> void: stuck = stk $Sprite2D.modulate = Color(0.75, 0.25, 1.0) if stuck else Color(1,1,1,1) var astar_grid: AStarGrid2D = $"..".astar_grid if astar_grid == null: return var source_coords: Vector2i = $"..".local_to_map(position) if stuck: astar_grid.set_point_weight_scale(source_coords, astar_grid.get_point_weight_scale(source_coords) * 50) else: astar_grid.set_point_weight_scale(source_coords, astar_grid.get_point_weight_scale(source_coords) / 50) func send_task_update(task_status: TaskStatus) -> void: task_updated.emit(task_status) func wait_for_task_status(task_status: TaskStatus) -> bool: while true: var status_received: TaskStatus = await task_updated; if status_received == task_status: return true elif status_received == TaskStatus.INTERRUPTED: return false return false func go_to_destination(destination: Vector2) -> bool: target = destination return await wait_for_task_status(TaskStatus.DESTINATION_REACHED) func kill() -> void: #var astar_grid: AStarGrid2D = $"..".astar_grid #var source_coords: Vector2i = $"..".local_to_map(position) #var scale: float = astar_grid.get_point_weight_scale(source_coords) #print("Tile %s weight scale read: %f" % [source_coords, scale]) #astar_grid.set_point_weight_scale(source_coords, scale + 0.1) var splat: Node2D = splat_scene.instantiate() splat.global_position = global_position add_sibling(splat) queue_free()