@icon('uid://br8ndde8qty32')
extends Node
class_name Entity


## Base entity class for everything not static.
##
## Should be the parent of every units, buildings,
## npc, loot, and everything that the player can interact with
##
## Is a node, because we want to deactivate rendering when out of camera and only simulating the world.
## Plus, only the entity is replicated in multiplayer

func _ready() -> void:
	add_to_group('entity')
	add_to_group('selectable-entity')


## Check if the entity is contained in the [Rect2] [param selection_box] of the player.
## Need the [Camera3D] of the player to work
func is_in_selection(selection_box:Rect2,player_cam:Camera3D) -> bool:
	if selection_box.size: # Checking if size is 0,0
		var p = player_cam.unproject_position(global_position)
		return selection_box.has_point(p)

	return false


## Check if the entity is under the raycast (usually made by [method SelectionManager.check_raycast])
## and colliding with entity bouding box
func is_under_mouse(raycast:Dictionary) -> bool:
	if raycast:
		var aabb:AABB = get_entity_aabb()
		return aabb.has_point(raycast.position)

	return false


## Calculate the node bouding box
func get_entity_aabb(node: Node3D = self, ignore_top_level: bool = true, bounds_transform: Transform3D = Transform3D()) -> AABB:
	if node == null: return AABB()
	var box: AABB
	var node_transform: Transform3D

	# we are going down the child chain, we want the aabb of each subsequent node to be on the same axis as the parent
	if bounds_transform.is_equal_approx(Transform3D()):
		node_transform = node.global_transform
	else:
		node_transform = bounds_transform

	# no more nodes. return default aabb
	if node == null:
		return AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4))

	# makes sure the transform we get isn't distorted
	var top_xform : Transform3D = node_transform.affine_inverse() * node.global_transform

	# convert the node into visualinstance3D to access get_aabb() function.
	var visual_result : VisualInstance3D = node as VisualInstance3D
	if visual_result != null:
		box = visual_result.get_aabb()
	else:
		box = AABB()

	# xforms the transform with the box aabb for proper alignment I believe?
	box = top_xform * box
	# recursion
	for i : int in node.get_child_count():
		var child : Node3D = node.get_child(i) as Node3D
		if child && !(ignore_top_level && child.top_level):
			var child_box : AABB = get_entity_aabb(child, ignore_top_level, transform)
			box = box.merge(child_box)

	return box


func select():
	add_to_group('selected-by-'+str(get_tree().get_multiplayer().get_unique_id()))
	print(self.name," selected"," by "+str(get_tree().get_multiplayer().get_unique_id()))


func deselect():
	remove_from_group('selected-by-'+str(get_tree().get_multiplayer().get_unique_id()))
	print(self.name," unselected")
