412 lines
18 KiB
GDScript
412 lines
18 KiB
GDScript
class_name Bugbot
|
|
extends CharacterBody3D
|
|
|
|
## A controllable bot that marks the location of a bug, and allows writing a bug
|
|
## report for it.
|
|
|
|
## Scene to use as the bug marker.
|
|
@export_file("*.scn","*.tscn") var bug_marker : String
|
|
|
|
#region InputEvent Exports
|
|
@export_group("Inputs")
|
|
@export_subgroup("Keyboard", "keyboard_")
|
|
@export var keyboard_move_forward : InputEventKey
|
|
@export var keyboard_move_backward : InputEventKey
|
|
@export var keyboard_move_left : InputEventKey
|
|
@export var keyboard_move_right : InputEventKey
|
|
@export var keyboard_move_up : InputEventKey
|
|
@export var keyboard_move_down : InputEventKey
|
|
|
|
@export var keyboard_tilt_up : InputEventKey
|
|
@export var keyboard_tilt_down : InputEventKey
|
|
@export var keyboard_pan_left : InputEventKey
|
|
@export var keyboard_pan_right : InputEventKey
|
|
|
|
@export var keyboard_movement_speed_up : InputEventKey
|
|
@export var keyboard_movement_speed_down : InputEventKey
|
|
|
|
@export var keyboard_place_marker : InputEventKey
|
|
@export var keyboard_exit_placement : InputEventKey
|
|
|
|
@export_subgroup("Mouse", "mouse_")
|
|
@export var mouse_movement_speed_up : InputEventMouseButton
|
|
@export var mouse_movement_speed_down : InputEventMouseButton
|
|
|
|
@export var mouse_place_marker : InputEventMouseButton
|
|
@export var mouse_exit_placement : InputEventMouseButton
|
|
|
|
@export_subgroup("Joypad", "joypad_")
|
|
@export var joypad_move_forward : InputEventJoypadMotion
|
|
@export var joypad_move_backward : InputEventJoypadMotion
|
|
@export var joypad_move_left : InputEventJoypadMotion
|
|
@export var joypad_move_right : InputEventJoypadMotion
|
|
@export var joypad_move_up : InputEventJoypadMotion
|
|
@export var joypad_move_down : InputEventJoypadMotion
|
|
|
|
@export var joypad_tilt_up : InputEventJoypadMotion
|
|
@export var joypad_tilt_down : InputEventJoypadMotion
|
|
@export var joypad_pan_left : InputEventJoypadMotion
|
|
@export var joypad_pan_right : InputEventJoypadMotion
|
|
|
|
@export var joypad_movement_speed_up : InputEventJoypadButton
|
|
@export var joypad_movement_speed_down : InputEventJoypadButton
|
|
|
|
@export var joypad_place_marker : InputEventJoypadButton
|
|
@export var joypad_exit_placement : InputEventJoypadButton
|
|
#endregion
|
|
|
|
#region Onready
|
|
@onready var __laser_beam : Node3D = $LaserBeamRoot as Node3D
|
|
@onready var __bug_marker_dummy : BugMarker = $LaserBeamRoot/LaserGlare/BugMarkerDummy as BugMarker
|
|
@onready var __movement_speed_change_timer : Timer = $MovementSpeedChangeTimer as Timer
|
|
@onready var __exit_placement_timer : Timer = $ExitPlacementTimer as Timer
|
|
@onready var __camera : Camera3D = $CollisionShape3D/Camera3D
|
|
#endregion
|
|
|
|
#region Constants
|
|
const DEFAULT_PRECISE_PLACEMENT_DISTANCE : float = 10.0
|
|
const DEFAULT_ARBITRARY_PLACEMENT_DISTANCE : float = 2.5
|
|
#endregion
|
|
|
|
#region Private Properties
|
|
static var __bugbot_server : BugbotServerAPI
|
|
static var __bug_marker_root : Node
|
|
|
|
var __bug_report_form : BugReportForm
|
|
|
|
var __movement_speed : float = 5.0
|
|
|
|
var __stored_camera : Camera3D
|
|
var __stored_mouse_mode : int = Input.MOUSE_MODE_CAPTURED
|
|
var __stored_pause_status : bool = false
|
|
var __raycast_collision : Dictionary
|
|
#endregion
|
|
|
|
#region Initialisation
|
|
func _enter_tree() -> void:
|
|
__stored_camera = get_viewport().get_camera_3d()
|
|
__stored_pause_status = get_tree().paused
|
|
get_tree().paused = true
|
|
__stored_mouse_mode = Input.mouse_mode
|
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
|
|
__bugbot_server = BugbotServerAPI._create_new_server_api()
|
|
|
|
|
|
#region Movement Configuration
|
|
InputMap.add_action(&"bugbot_move_forward", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_forward", keyboard_move_forward)
|
|
InputMap.action_add_event(&"bugbot_move_forward", joypad_move_forward)
|
|
InputMap.add_action(&"bugbot_move_backward", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_backward", keyboard_move_backward)
|
|
InputMap.action_add_event(&"bugbot_move_backward", joypad_move_backward)
|
|
InputMap.add_action(&"bugbot_move_left", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_left", keyboard_move_left)
|
|
InputMap.action_add_event(&"bugbot_move_left", joypad_move_left)
|
|
InputMap.add_action(&"bugbot_move_right", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_right", keyboard_move_right)
|
|
InputMap.action_add_event(&"bugbot_move_right", joypad_move_right)
|
|
InputMap.add_action(&"bugbot_move_down", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_down", keyboard_move_down)
|
|
InputMap.action_add_event(&"bugbot_move_down", joypad_move_down)
|
|
InputMap.add_action(&"bugbot_move_up", 0.1)
|
|
InputMap.action_add_event(&"bugbot_move_up", keyboard_move_up)
|
|
InputMap.action_add_event(&"bugbot_move_up", joypad_move_up)
|
|
#endregion
|
|
|
|
#region Camera Configuration
|
|
InputMap.add_action(&"bugbot_tilt_up", 0.1)
|
|
InputMap.action_add_event(&"bugbot_tilt_up", keyboard_tilt_up)
|
|
InputMap.action_add_event(&"bugbot_tilt_up", joypad_tilt_up)
|
|
InputMap.add_action(&"bugbot_tilt_down", 0.1)
|
|
InputMap.action_add_event(&"bugbot_tilt_down", keyboard_tilt_down)
|
|
InputMap.action_add_event(&"bugbot_tilt_down", joypad_tilt_down)
|
|
InputMap.add_action(&"bugbot_pan_left", 0.1)
|
|
InputMap.action_add_event(&"bugbot_pan_left", keyboard_pan_left)
|
|
InputMap.action_add_event(&"bugbot_pan_left", joypad_pan_left)
|
|
InputMap.add_action(&"bugbot_pan_right", 0.1)
|
|
InputMap.action_add_event(&"bugbot_pan_right", keyboard_pan_right)
|
|
InputMap.action_add_event(&"bugbot_pan_right", joypad_pan_right)
|
|
#endregion
|
|
|
|
#region Speed Configuration
|
|
InputMap.add_action(&"bugbot_movement_speed_up")
|
|
InputMap.action_add_event(&"bugbot_movement_speed_up", keyboard_movement_speed_up)
|
|
InputMap.action_add_event(&"bugbot_movement_speed_up", mouse_movement_speed_up)
|
|
InputMap.action_add_event(&"bugbot_movement_speed_up", joypad_movement_speed_up)
|
|
InputMap.add_action(&"bugbot_movement_speed_down")
|
|
InputMap.action_add_event(&"bugbot_movement_speed_down", keyboard_movement_speed_down)
|
|
InputMap.action_add_event(&"bugbot_movement_speed_down", mouse_movement_speed_down)
|
|
InputMap.action_add_event(&"bugbot_movement_speed_down", joypad_movement_speed_down)
|
|
#endregion
|
|
|
|
#region Marker Placement Configuration
|
|
InputMap.add_action(&"bugbot_place_marker")
|
|
InputMap.action_add_event(&"bugbot_place_marker", keyboard_place_marker)
|
|
InputMap.action_add_event(&"bugbot_place_marker", mouse_place_marker)
|
|
InputMap.action_add_event(&"bugbot_place_marker", joypad_place_marker)
|
|
|
|
InputMap.add_action(&"bugbot_exit_placement")
|
|
InputMap.action_add_event(&"bugbot_exit_placement", keyboard_exit_placement)
|
|
InputMap.action_add_event(&"bugbot_exit_placement", mouse_exit_placement)
|
|
InputMap.action_add_event(&"bugbot_exit_placement", joypad_exit_placement)
|
|
#endregion
|
|
|
|
func _exit_tree() -> void:
|
|
#region Movement Deconfiguration
|
|
InputMap.action_erase_event(&"bugbot_move_forward", keyboard_move_forward)
|
|
InputMap.action_erase_event(&"bugbot_move_forward", joypad_move_forward)
|
|
InputMap.action_erase_event(&"bugbot_move_backward", keyboard_move_backward)
|
|
InputMap.action_erase_event(&"bugbot_move_backward", joypad_move_backward)
|
|
InputMap.action_erase_event(&"bugbot_move_left", keyboard_move_left)
|
|
InputMap.action_erase_event(&"bugbot_move_left", joypad_move_left)
|
|
InputMap.action_erase_event(&"bugbot_move_right", keyboard_move_right)
|
|
InputMap.action_erase_event(&"bugbot_move_right", joypad_move_right)
|
|
InputMap.action_erase_event(&"bugbot_move_down", keyboard_move_down)
|
|
InputMap.action_erase_event(&"bugbot_move_down", joypad_move_down)
|
|
InputMap.action_erase_event(&"bugbot_move_up", keyboard_move_up)
|
|
InputMap.action_erase_event(&"bugbot_move_up", joypad_move_up)
|
|
#endregion
|
|
|
|
#region Camera Deconfiguration
|
|
InputMap.action_erase_event(&"bugbot_tilt_up", keyboard_tilt_up)
|
|
InputMap.action_erase_event(&"bugbot_tilt_up", joypad_tilt_up)
|
|
InputMap.action_erase_event(&"bugbot_tilt_down", keyboard_tilt_down)
|
|
InputMap.action_erase_event(&"bugbot_tilt_down", joypad_tilt_down)
|
|
InputMap.action_erase_event(&"bugbot_pan_left", keyboard_pan_left)
|
|
InputMap.action_erase_event(&"bugbot_pan_left", joypad_pan_left)
|
|
InputMap.action_erase_event(&"bugbot_pan_right", keyboard_pan_right)
|
|
InputMap.action_erase_event(&"bugbot_pan_right", joypad_pan_right)
|
|
#endregion
|
|
|
|
#region Speed Deconfiguration
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_up", keyboard_movement_speed_up)
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_up", mouse_movement_speed_up)
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_up", joypad_movement_speed_up)
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_down", keyboard_movement_speed_down)
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_down", mouse_movement_speed_down)
|
|
InputMap.action_erase_event(&"bugbot_movement_speed_down", joypad_movement_speed_down)
|
|
#endregion
|
|
|
|
#region Marker Placement Deconfiguration
|
|
InputMap.action_erase_event(&"bugbot_place_marker", keyboard_place_marker)
|
|
InputMap.action_erase_event(&"bugbot_place_marker", mouse_place_marker)
|
|
InputMap.action_erase_event(&"bugbot_place_marker", joypad_place_marker)
|
|
|
|
InputMap.action_erase_event(&"bugbot_exit_placement", keyboard_exit_placement)
|
|
InputMap.action_erase_event(&"bugbot_exit_placement", mouse_exit_placement)
|
|
InputMap.action_erase_event(&"bugbot_exit_placement", joypad_exit_placement)
|
|
#endregion
|
|
|
|
#region Action Removal
|
|
InputMap.erase_action(&"bugbot_move_forward")
|
|
InputMap.erase_action(&"bugbot_move_backward")
|
|
InputMap.erase_action(&"bugbot_move_left")
|
|
InputMap.erase_action(&"bugbot_move_right")
|
|
InputMap.erase_action(&"bugbot_move_down")
|
|
InputMap.erase_action(&"bugbot_move_up")
|
|
InputMap.erase_action(&"bugbot_tilt_up")
|
|
InputMap.erase_action(&"bugbot_tilt_down")
|
|
InputMap.erase_action(&"bugbot_pan_left")
|
|
InputMap.erase_action(&"bugbot_pan_right")
|
|
InputMap.erase_action(&"bugbot_movement_speed_up")
|
|
InputMap.erase_action(&"bugbot_movement_speed_down")
|
|
InputMap.erase_action(&"bugbot_place_marker")
|
|
InputMap.erase_action(&"bugbot_exit_placement")
|
|
#endregion
|
|
|
|
Input.mouse_mode = __stored_mouse_mode
|
|
get_tree().paused = __stored_pause_status
|
|
|
|
func _ready() -> void:
|
|
__camera.current = true
|
|
if __stored_camera:
|
|
global_transform = __stored_camera.global_transform
|
|
__camera.fov = __stored_camera.fov
|
|
__camera.near = __stored_camera.near
|
|
__camera.far = __stored_camera.far
|
|
|
|
__bug_marker_dummy.enable_info = false
|
|
#endregion
|
|
|
|
func _process(_delta:float) -> void:
|
|
__calculate_movement_speed()
|
|
__calculate_rotation(_delta)
|
|
__calculate_movement()
|
|
__place_dummy_marker()
|
|
|
|
func _physics_process(_delta:float) -> void:
|
|
__raycast_to_world()
|
|
__calculate_button_presses()
|
|
move_and_slide()
|
|
|
|
|
|
static func instantiate(tree:SceneTree) -> void:
|
|
tree.root.add_child(load("res://addons/Bugbot/Scenes/bugbot_player.tscn").instantiate() as CharacterBody3D)
|
|
|
|
static func load_bug_markers(marker_root:Node, bug_list:Array) -> void:
|
|
var bug_marker_preload : PackedScene = preload("res://addons/Bugbot/Scenes/bug_marker.tscn")
|
|
|
|
Bugbot.adjust_bug_marker_colours()
|
|
|
|
for bug_info:BugbotBugData in bug_list:
|
|
if not bug_info: continue
|
|
|
|
var bug_status : BugMarker.BugStatus = BugMarker.BugStatus.NEUTRAL
|
|
|
|
if bug_info.resolution == BugbotServerGiteaAPI.RESOLVED_TAG:
|
|
bug_status = BugMarker.BugStatus.RESOLVED
|
|
elif bug_info.resolution == BugbotServerGiteaAPI.IN_PROGRESS_TAG:
|
|
bug_status = BugMarker.BugStatus.IN_PROGRESS
|
|
elif bug_info.resolution == BugbotServerGiteaAPI.UNRESOLVED_TAG:
|
|
bug_status = BugMarker.BugStatus.UNRESOLVED
|
|
|
|
var bug_marker : BugMarker = bug_marker_preload.instantiate() as BugMarker
|
|
marker_root.add_child(bug_marker)
|
|
bug_marker.marker_status = bug_status
|
|
bug_marker.bug_info = bug_info
|
|
bug_marker.global_position = bug_info.marker_position
|
|
bug_marker.set_rotation_to_normal(bug_info.marker_normal)
|
|
|
|
static func adjust_bug_marker_colours() -> void:
|
|
var unresolved_colour : Color = ProjectSettings.get_setting("bugbot/markers/unresolved/tint", BugMarker.UNRESOLVED_TINT)
|
|
var in_progress_colour : Color = ProjectSettings.get_setting("bugbot/markers/in_progress/tint", BugMarker.IN_PROGRESS_TINT)
|
|
var resolved_colour : Color = ProjectSettings.get_setting("bugbot/markers/resolved/tint", BugMarker.RESOLVED_TINT)
|
|
|
|
var unresolved_material : BugMarkerMaterialPack = load("res://addons/Bugbot/Materials/BugMarker/bug_marker_unresolved.res")
|
|
(unresolved_material.arrow as ShaderMaterial).set_shader_parameter("colour", unresolved_colour)
|
|
(unresolved_material.icon as ShaderMaterial).set_shader_parameter("colour", unresolved_colour)
|
|
|
|
var in_progress_material : BugMarkerMaterialPack = load("res://addons/Bugbot/Materials/BugMarker/bug_marker_in_progress.res")
|
|
(in_progress_material.arrow as ShaderMaterial).set_shader_parameter("colour", in_progress_colour)
|
|
(in_progress_material.icon as ShaderMaterial).set_shader_parameter("colour", in_progress_colour)
|
|
|
|
var resolved_material : BugMarkerMaterialPack = load("res://addons/Bugbot/Materials/BugMarker/bug_marker_resolved.res")
|
|
(resolved_material.arrow as ShaderMaterial).set_shader_parameter("colour", resolved_colour)
|
|
(resolved_material.icon as ShaderMaterial).set_shader_parameter("colour", resolved_colour)
|
|
|
|
static func __get_bug_marker_root(current_scene:Node) -> void:
|
|
var current_scene_hash : String = current_scene.scene_file_path.sha256_text()
|
|
var current_scene_children : Array = current_scene.get_children()
|
|
var found_root : bool = false
|
|
for child:Node in current_scene_children:
|
|
if child.name == current_scene_hash:
|
|
__bug_marker_root = child
|
|
return
|
|
var new_root : Node = Node.new()
|
|
current_scene.add_child(new_root)
|
|
new_root.name = current_scene_hash
|
|
__bug_marker_root = new_root
|
|
|
|
static func show_bug_markers(current_scene:Node) -> void:
|
|
Bugbot.__get_bug_marker_root(current_scene)
|
|
__bugbot_server._return_list_of_bugs(current_scene.scene_file_path, __show_bug_markers_response)
|
|
static func __show_bug_markers_response(bugs:Array) -> void:
|
|
Bugbot.load_bug_markers(__bug_marker_root, bugs)
|
|
|
|
static func hide_bug_markers(current_scene:Node) -> void:
|
|
Bugbot.__get_bug_marker_root(current_scene)
|
|
__bug_marker_root.queue_free()
|
|
|
|
func __calculate_movement_speed() -> void:
|
|
if Input.is_action_pressed(&"bugbot_movement_speed_down") or Input.is_action_just_pressed(&"bugbot_movement_speed_down"):
|
|
if __movement_speed_change_timer.is_stopped():
|
|
__movement_speed /= 1.25
|
|
__movement_speed_change_timer.start()
|
|
elif Input.is_action_pressed(&"bugbot_movement_speed_up") or Input.is_action_just_pressed(&"bugbot_movement_speed_up"):
|
|
if __movement_speed_change_timer.is_stopped():
|
|
__movement_speed *= 1.25
|
|
__movement_speed_change_timer.start()
|
|
else:
|
|
__movement_speed_change_timer.stop()
|
|
__movement_speed = clampf(__movement_speed, 0.25, 100.0)
|
|
|
|
func __calculate_rotation(_delta:float) -> void:
|
|
var rotation_velocity : Vector2 = Vector2(
|
|
Input.get_axis(&"bugbot_tilt_down", &"bugbot_tilt_up"),
|
|
Input.get_axis(&"bugbot_pan_right", &"bugbot_pan_left")) * 2.5
|
|
var mouse_velocity : Vector2 = Input.get_last_mouse_velocity()
|
|
rotation_velocity.x += mouse_velocity.y * -0.0025
|
|
rotation_velocity.y += mouse_velocity.x * -0.0025
|
|
rotation.x = clampf(rotation.x + (rotation_velocity.x * _delta), -PI/2.0 + 0.0001, PI/2.0 - 0.0001)
|
|
rotation.y += rotation_velocity.y * _delta
|
|
|
|
func __calculate_movement() -> void:
|
|
var movement_vector_lateral : Vector2 = Input.get_vector(&"bugbot_move_left", &"bugbot_move_right", &"bugbot_move_forward", &"bugbot_move_backward")
|
|
var movement_azimuth : float = Input.get_axis(&"bugbot_move_down", &"bugbot_move_up")
|
|
velocity = transform.basis * Vector3(movement_vector_lateral.x, movement_azimuth, movement_vector_lateral.y) * __movement_speed
|
|
|
|
func __calculate_button_presses() -> void:
|
|
if Input.is_action_just_pressed(&"bugbot_place_marker") and __raycast_collision:
|
|
__select_at_location(__raycast_collision["position"], __raycast_collision["normal"])
|
|
|
|
if Input.is_action_just_released(&"bugbot_exit_placement"):
|
|
__exit_placement_timer.stop()
|
|
elif Input.is_action_just_pressed(&"bugbot_exit_placement"):
|
|
__exit_placement_timer.start()
|
|
func __on_exit_placement_timer_timeout() -> void:
|
|
queue_free()
|
|
|
|
func __raycast_to_world() -> void:
|
|
var ray_length : float = ProjectSettings.get_setting("bugbot/bug_placement/precise_placement_distance", DEFAULT_PRECISE_PLACEMENT_DISTANCE)
|
|
var space : PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
|
|
var query : PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(global_position, global_position - global_transform.basis.z * ray_length)
|
|
query.collide_with_areas = true
|
|
query.collide_with_bodies = true
|
|
query.exclude = [get_rid()]
|
|
__raycast_collision = space.intersect_ray(query)
|
|
if __raycast_collision:
|
|
var collision_point : Vector3 = __raycast_collision["position"]
|
|
var distance_to_collision : float = __laser_beam.global_position.distance_to(collision_point)
|
|
__laser_beam.scale.z = distance_to_collision
|
|
__laser_beam.look_at(collision_point)
|
|
__laser_beam.visible = true
|
|
else:
|
|
__laser_beam.visible = false
|
|
|
|
|
|
func __select_at_location(_position:Vector3, _normal:Vector3) -> void:
|
|
var collider : BugInfoCollider = __raycast_collision["collider"] as BugInfoCollider
|
|
if collider:
|
|
__pop_up_marker_info(collider, _position, _normal)
|
|
else:
|
|
__place_marker(_position, _normal)
|
|
|
|
func __place_marker(_position:Vector3, _normal:Vector3) -> void:
|
|
process_mode = Node.PROCESS_MODE_PAUSABLE
|
|
__bugbot_server._prepare_form(__bug_report_form_data_prepared)
|
|
|
|
func __pop_up_marker_info(_collider:BugInfoCollider, _position:Vector3, _normal:Vector3) -> void:
|
|
print(_collider.bug_info.title)
|
|
|
|
|
|
func __bug_report_form_data_prepared(tag_lists:Array) -> void:
|
|
__bug_report_form = preload("res://addons/Bugbot/UI/bug_report_form.tscn").instantiate()
|
|
add_child(__bug_report_form)
|
|
__bug_report_form.map_name = get_tree().current_scene.scene_file_path
|
|
__bug_report_form.bug_location = __raycast_collision["position"]
|
|
__bug_report_form.bug_rotation = __raycast_collision["normal"]
|
|
__bug_report_form.fill_tags(tag_lists)
|
|
__bug_report_form.submitted.connect(__form_submitted)
|
|
__bug_report_form.cancelled.connect(__form_cancelled)
|
|
func __form_submitted(bug:BugbotBugData) -> void:
|
|
__get_bug_marker_root(get_tree().current_scene)
|
|
var marker : BugMarker = (load(bug_marker) as PackedScene).instantiate() as BugMarker
|
|
__bug_marker_root.add_child(marker)
|
|
marker.global_position = bug.marker_position
|
|
marker.set_rotation_to_normal(bug.marker_normal)
|
|
marker.marker_status = BugMarker.BugStatus.UNRESOLVED
|
|
marker.bug_info = bug
|
|
__form_cancelled()
|
|
func __form_cancelled() -> void:
|
|
__bug_report_form.queue_free()
|
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
|
|
|
|
|
func __place_dummy_marker() -> void:
|
|
if __raycast_collision:
|
|
__bug_marker_dummy.set_rotation_to_normal(__raycast_collision["normal"])
|
|
__bug_marker_dummy.visible = true
|
|
else:
|
|
__bug_marker_dummy.visible = false
|