@tool
extends CharacterBody3D

@export var speed = 5.0
@export var input: Node

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting(&"physics/3d/default_gravity")

func _get_rollback_state_properties() -> Array:
	return [
		"transform",
		"velocity"
	]

func _get_interpolated_properties() -> Array:
	return [
		"transform"
	]

func _ready():
	if Engine.is_editor_hint(): return

	if input == null:
		input = $Input

	position = Vector3(0, 4, 0)

	# Assign a random color
	var player_id := input.get_multiplayer_authority()
	var mesh := $MeshInstance3D as MeshInstance3D

	var color := Color.from_hsv((hash(player_id) % 256) / 256.0, 1.0, 1.0)
	var material := mesh.get_active_material(0) as StandardMaterial3D
	material = material.duplicate()
	material.albedo_color = color
	mesh.set_surface_override_material(0, material)

func _rollback_tick(dt, _tick, _is_fresh: bool):
	# Add gravity
	_force_update_is_on_floor()
	if not is_on_floor():
		velocity.y -= gravity * dt

	var input_dir = input.movement
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.z)).normalized()
	if direction:
		velocity.x = direction.x * speed
		velocity.z = direction.z * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)
		velocity.z = move_toward(velocity.z, 0, speed)

	# move_and_slide assumes physics delta
	# multiplying velocity by NetworkTime.physics_factor compensates for it
	velocity *= NetworkTime.physics_factor
	move_and_slide()
	velocity /= NetworkTime.physics_factor

func _force_update_is_on_floor():
	var old_velocity = velocity
	velocity = Vector3.ZERO
	move_and_slide()
	velocity = old_velocity

