tli/scripts/units/components/gathering.gd

280 lines
6.5 KiB
GDScript

extends Area3D
class_name Gathering
const ANTHILL_DEPOSIT_RADIUS: float = 0.75
enum State {
WAITING_FOR_NEW_ITEMS,
WAITING_FOR_MORE_SPACE,
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("gather_targ", _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.anthill.buy_ant.connect(_on_anthill_buy_ant)
_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 _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.WAITING_FOR_MORE_SPACE
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.WAITING_FOR_NEW_ITEMS
var nearest_item := _find_nearest(_nearby_items.values())
if nearest_item != null:
_go_pick_up(nearest_item)
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())
_unit.animation_tree["parameters/plop_down_oneshot/request"] = (
AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
)
await get_tree().create_timer(_pickup_interval).timeout
if state == State.STOP:
return
var nearest := _find_nearest(_nearby_items.values())
if _carrying_items.size() == _max_carrying or nearest == null:
_go_deposit()
return
_go_pick_up(nearest)
func _deposit() -> void:
while _carrying_items.size() > 0:
if state != State.DEPOSITING:
return
if _unit.anthill.space_left() == 0:
state = State.WAITING_FOR_MORE_SPACE
return
var item := _carrying_items.pop_back() as Honeydew
_unit.animation_tree["parameters/plop_up_oneshot/request"] = (
AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
)
audio_player.play_sound(SoundManager.swoosh())
await item.start_tweening(
_unit.anthill.deposit_point.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 state == State.STOP:
return
var nearest := _find_nearest(_nearby_items.values())
if nearest == null:
state = State.WAITING_FOR_NEW_ITEMS
_unit.navigate(gathering_center)
return
_go_pick_up(nearest)
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.WAITING_FOR_NEW_ITEMS
or state == State.WAITING_FOR_MORE_SPACE
):
_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
# and global_position.distance_to(_unit.anthill.global_position) < 2
):
_deposit()
func _on_anthill_buy_ant() -> void:
if state != State.WAITING_FOR_MORE_SPACE:
return
var nearest := _find_nearest(_nearby_items.values())
if (
_carrying_items.size() == _max_carrying
or (_carrying_items.size() > 0 and nearest == null)
):
_go_deposit()
return
if nearest != null:
_go_pick_up(nearest)
else:
state = State.WAITING_FOR_NEW_ITEMS