extends Area3D class_name Gathering const ANTHILL_DEPOSIT_RADIUS: float = 0.5 enum State { AWAITING, PICKING_UP, DEPOSITING, STOP, } var state: State = State.STOP var _unit: ControlledUnit var _nearby_items: Dictionary = {} var _carrying_items: Array[Honeydew] = [] var _max_carrying: int = 0 var _target: Honeydew var _skeleton: Skeleton3D var _drop_interval: float = 0 var _pickup_interval: float = 0 var _item_bones: Array[int] = [] var _showing_after_set: bool = false @onready var gathering_center: Vector3 = global_position @onready var collision_shape: CollisionShape3D = $NearbyItemsSearch @onready var radius_indicator: VisualInstance3D = ( $NearbyItemsSearch/GatheringRadius ) @onready var audio_player: SoundEffectsPlayer = ( $SoundEffectsPlayer ) func _ready() -> void: assert(collision_shape != null, "collision_shape missing!") assert(radius_indicator != null, "radius_indicator missing!") assert(audio_player != null, "audio_player missing!") body_entered.connect(_on_body_entered) body_exited.connect(_on_body_exited) func _process(_delta: float) -> void: for i in range(_carrying_items.size()): var item := _carrying_items[i] item.global_position = _get_nth_pile_pos(i) collision_shape.global_position = gathering_center collision_shape.global_rotation = Vector3.ZERO radius_indicator.visible = ( (state != State.STOP and _unit.showing_info) or _showing_after_set ) if _target != null: DebugManager.circle(_target.global_position) func _input(event: InputEvent) -> void: if event is InputEventMouseButton and _showing_after_set: var button_event := event as InputEventMouseButton if not button_event.pressed: return if ( button_event.button_index == MOUSE_BUTTON_LEFT or button_event.button_index == MOUSE_BUTTON_RIGHT ): _showing_after_set = false func initialize( unit: ControlledUnit, skeleton_3d: Skeleton3D, bones: Array[int], max_carry: int, drop_interv: float, pickup_interv: float, ) -> void: _unit = unit _max_carrying = max_carry _drop_interval = drop_interv _pickup_interval = pickup_interv _skeleton = skeleton_3d _item_bones = bones _unit.moving_started.connect(_on_unit_moving_started) _unit.nav_agent.navigation_finished.connect( _on_nav_agent_navigation_finished ) func start_gathering(item: Honeydew) -> void: gathering_center = item.global_position _showing_after_set = true _go_pick_up(item) func _go_pick_up(item: Honeydew) -> void: if _unit.anthill.space_left() <= 0: state = State.AWAITING return if _carrying_items.size() >= _max_carrying: _go_deposit() return _target = item state = State.PICKING_UP _unit.navigate(item.global_position) func _go_deposit() -> void: if _unit.anthill.space_left() <= 0: state = State.AWAITING return state = State.DEPOSITING var dir := _unit.anthill.global_position.direction_to(global_position) _unit.navigate( _unit.anthill.global_position + dir * ANTHILL_DEPOSIT_RADIUS ) func _get_nth_pile_pos(n: int) -> Vector3: return _skeleton.to_global( _skeleton.get_bone_global_pose(_item_bones[n]).origin, ) func _pick_up() -> void: if _target == null or _target.carried: state = State.AWAITING if _nearby_items.size() != 0: _go_pick_up(_find_nearest(_nearby_items.values())) elif _carrying_items.size() > 0: _go_deposit() return _carrying_items.append(_target) _target.set_carried(true) audio_player.play_sound(SoundManager.swoosh()) await _target.start_tweening( _get_nth_pile_pos(_carrying_items.size() - 1) ).tween_finished audio_player.play_sound(SoundManager.pop()) await get_tree().create_timer(_pickup_interval).timeout if _carrying_items.size() >= _max_carrying or _nearby_items.size() == 0: _go_deposit() return _go_pick_up(_find_nearest(_nearby_items.values())) func _deposit() -> void: await get_tree().create_timer(0.5).timeout while _carrying_items.size() > 0: if state != State.DEPOSITING: return if _unit.anthill.space_left() <= 0: state = State.AWAITING return var item := _carrying_items.pop_back() as Honeydew audio_player.play_sound(SoundManager.swoosh()) await item.start_tweening(_unit.anthill.global_position).tween_finished audio_player.play_sound(SoundManager.tok()) item.remove_from_spawner() _remove_honeydew_from_nearby(item) item.queue_free() _unit.anthill.deposit_honeydew(1) await get_tree().create_timer(_drop_interval).timeout if _nearby_items.size() == 0: state = State.AWAITING _unit.navigate(gathering_center) return _go_pick_up(_find_nearest(_nearby_items.values())) func _find_nearest(items: Array) -> Honeydew: var nearest: Honeydew = null var nearest_distance: float = INF for item: Honeydew in items: if item.carried: continue var distance := global_position.distance_squared_to(item.global_position) if distance < nearest_distance: nearest_distance = distance nearest = item return nearest func _remove_honeydew_from_nearby(item: Honeydew) -> void: var item_id := item.get_instance_id() if not _nearby_items.keys().has(item_id): return _nearby_items.erase(item_id) func _on_body_entered(item: Node3D) -> void: if item is not Honeydew: return var item_id := item.get_instance_id() if _nearby_items.keys().has(item_id): return _nearby_items[item_id] = item as Honeydew if state == State.AWAITING: _go_pick_up(item as Honeydew) func _on_body_exited(item: Node3D) -> void: if item is not Honeydew: return _remove_honeydew_from_nearby(item as Honeydew) func _on_unit_moving_started() -> void: state = State.STOP _target = null func _on_nav_agent_navigation_finished() -> void: if state == State.PICKING_UP: _pick_up() if state == State.DEPOSITING: _deposit()