class_name SliceRing extends Node3D @export var slice_ring_mesh : MeshInstance3D @export_file("*.scn", "*.tscn") var slice_ring_hurtbox : String @export_category("Input") @export var slice_ring_input : InputResource @export var _joystick_tilt_threshold : float = 0.6 @export var _flick_threshold : float = 0.15 @export_range(0.01, 0.1, 0.001) var _flick_detection_timer_delay : float = 0.05 @export var _rotation_lerp_speed : float = 20.0 @export var _random_slice_positions_horizontal : Array[float] @export var _random_slice_positions_vertical : Array[float] @export_range(0.02, 1.0, 0.02) var _max_time_between_cuts : float = 0.12 @export_category("Visuals") @export var _high_power_material : Material @export var _low_power_material : Material @onready var _flick_timer : Timer = $FlickTimer @onready var _slice_timer : Timer = $SliceTimer signal slice_aim_update(bool, float) signal slice_executed var _is_aiming_slice : bool = false var _last_angle_vector_length : float = 0.0 func _ready() -> void: slice_ring_mesh.quaternion = Quaternion.IDENTITY InputHandler.add_input_resource(self, slice_ring_input) func set_high_power(_high_power:bool) -> void: slice_ring_mesh.mesh.surface_set_material(0, _high_power_material if _high_power else _low_power_material) func _calculate_slice_angle(_delta:float, _vector:Vector2, _tilt_input:bool) -> void: var angle_length : float = _vector.length() if _slice_timer.is_stopped(): if angle_length >= _joystick_tilt_threshold or not _tilt_input: var normalised_vector : Vector2 = _vector.normalized() var angle : float = atan2(normalised_vector.y, normalised_vector.x) slice_ring_mesh.quaternion = lerp(slice_ring_mesh.quaternion, Quaternion(basis.z, angle), _delta * angle_length * _rotation_lerp_speed) _is_aiming_slice = true elif _tilt_input and _last_angle_vector_length >= _joystick_tilt_threshold: if _flick_timer.is_inside_tree() and _flick_timer.is_stopped() and _last_angle_vector_length >= _joystick_tilt_threshold: _flick_timer.start(_flick_detection_timer_delay) emit_signal("slice_aim_update", _is_aiming_slice, slice_ring_mesh.rotation.z) _last_angle_vector_length = angle_length func _on_flick_timer_timeout() -> void: if _last_angle_vector_length < _flick_threshold: _execute_slice() _is_aiming_slice = false emit_signal("slice_aim_update", _is_aiming_slice, 0.0) func _execute_slice(_treat_as_flick:bool = true, _vertical_slices:bool = false) -> void: slice_ring_mesh.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_INHERIT if _treat_as_flick else Node.PHYSICS_INTERPOLATION_MODE_OFF if _slice_timer.is_inside_tree() and _slice_timer.is_stopped(): if not _treat_as_flick and not _is_aiming_slice: var random_roll : float = 0.0 if _vertical_slices: if _random_slice_positions_vertical.size() > 0: random_roll = _random_slice_positions_vertical[0] _random_slice_positions_vertical.remove_at(0) _random_slice_positions_vertical.append(random_roll) else: random_roll = 90.0 else: if _random_slice_positions_horizontal.size() > 0: random_roll = _random_slice_positions_horizontal[0] _random_slice_positions_horizontal.remove_at(0) _random_slice_positions_horizontal.append(random_roll) else: random_roll = 90.0 slice_ring_mesh.quaternion = Quaternion(Vector3.FORWARD, deg_to_rad(random_roll)) var _slice_ring_hurtbox_node : HurtboxBase = load(slice_ring_hurtbox).instantiate() if _slice_ring_hurtbox_node: _slice_ring_hurtbox_node.global_transform = slice_ring_mesh.global_transform get_tree().root.add_child(_slice_ring_hurtbox_node) emit_signal("slice_executed") _slice_timer.start() func _on_slice_timer_timeout() -> void: pass func _on_horizontal_slice_pressed() -> void: _execute_slice(false, false) func _on_vertical_slice_pressed() -> void: _execute_slice(false, true) func _on_aim_slice_ring_2d(_delta:float, _vector:Vector2) -> void: _calculate_slice_angle(_delta, _vector, true) func _on_aim_slice_ring_mouse(_delta:float, _vector:Vector2) -> void: _calculate_slice_angle(_delta, _vector, false)