#* #* showcase.gd #* ============================================================================= #* Copyright 2024 Serhii Snitsaruk #* #* Use of this source code is governed by an MIT-style #* license that can be found in the LICENSE file or at #* https://opensource.org/licenses/MIT. #* ============================================================================= #* extends Node2D @onready var behavior_tree_view: BehaviorTreeView = %BehaviorTreeView @onready var camera: Camera2D = $Camera2D @onready var agent_selection: MenuButton = %AgentSelection @onready var previous: Button = %Previous @onready var next: Button = %Next @onready var minimize_description: Button = %MinimizeDescription @onready var description: RichTextLabel = %Description @onready var begin_tutorial: Button = %BeginTutorial @onready var navigation_hint: Label = %NavigationHint @onready var scene_title: Label = %SceneTitle @onready var code_popup = %CodePopup @onready var code_edit = %CodeEdit var bt_player: BTPlayer var selected_tree_index: int = -1 var agent_files: Array[String] var agents_dir: String var is_tutorial: bool = false func _ready() -> void: code_popup.hide() agent_selection.get_popup().id_pressed.connect(_on_agent_selection_id_pressed) previous.pressed.connect(func(): _on_agent_selection_id_pressed(selected_tree_index - 1)) next.pressed.connect(func(): _on_agent_selection_id_pressed(selected_tree_index + 1)) _initialize() func _physics_process(_delta: float) -> void: var inst: BTInstance = bt_player.get_bt_instance() var bt_data: BehaviorTreeData = BehaviorTreeData.create_from_bt_instance(inst) behavior_tree_view.update_tree(bt_data) func _initialize() -> void: if is_tutorial: _populate_agent_files("res://demo/agents/tutorial/") begin_tutorial.text = "End Tutorial" navigation_hint.text = "Navigate ➤" scene_title.text = "Tutorial" _on_agent_selection_id_pressed(0) else: _populate_agent_files("res://demo/agents/") begin_tutorial.text = "Begin Tutorial" navigation_hint.text = "Select Agent ➤" scene_title.text = "Showcase" _on_agent_selection_id_pressed(0) func _attach_camera(agent: CharacterBody2D) -> void: await get_tree().process_frame camera.get_parent().remove_child(camera) agent.add_child(camera) camera.position = Vector2(400.0, 0.0) func _populate_agent_files(p_path: String = "res://demo/agents/") -> void: var popup: PopupMenu = agent_selection.get_popup() popup.clear() popup.reset_size() agent_files.clear() agents_dir = p_path var dir := DirAccess.open(p_path) if dir: dir.list_dir_begin() var file_name: String = dir.get_next() while file_name != "": if dir.current_is_dir() or file_name.begins_with("agent_base"): file_name = dir.get_next() continue agent_files.append(file_name.get_file().trim_suffix(".remap")) file_name = dir.get_next() dir.list_dir_end() agent_files.sort() for i in agent_files.size(): popup.add_item(agent_files[i], i) func _load_agent(file_name: String) -> void: var agent_res := load(file_name) as PackedScene assert(agent_res != null) for child in get_children(): if child is CharacterBody2D and child.name != "Dummy": child.die() var agent: CharacterBody2D = agent_res.instantiate() add_child(agent) bt_player = agent.find_child("BTPlayer") description.text = _parse_description(bt_player.behavior_tree.description) _attach_camera(agent) func _parse_description(p_desc: String) -> String: return p_desc \ .replace("[SUCCESS]", "[color=PaleGreen]SUCCESS[/color]") \ .replace("[FAILURE]", "[color=IndianRed]FAILURE[/color]") \ .replace("[RUNNING]", "[color=orange]RUNNING[/color]") \ .replace("[comp]", "[color=CornflowerBlue][b]") \ .replace("[/comp]", "[/b][/color]") \ .replace("[act]", "[color=white][b]") \ .replace("[/act]", "[/b][/color]") \ .replace("[dec]", "[color=MediumOrchid][b]") \ .replace("[/dec]", "[/b][/color]") \ .replace("[con]", "[color=orange][b]") \ .replace("[/con]", "[/b][/color]") func _on_agent_selection_id_pressed(id: int) -> void: assert(id >= 0 and id < agent_files.size()) selected_tree_index = id _load_agent(agents_dir.path_join(agent_files[id])) agent_selection.text = bt_player.behavior_tree.resource_path.get_file() if agent_selection.text.to_lower() != agent_selection.text: # Treat filename as a title agent_selection.text = agent_selection.text.trim_suffix(".tres") previous.disabled = id == 0 next.disabled = id == (agent_files.size() - 1) func _on_switch_to_game_pressed() -> void: get_tree().change_scene_to_file("res://demo/scenes/game.tscn") func _on_minimize_description_button_down() -> void: description.visible = not description.visible minimize_description.text = "-" if description.visible else "+" func _on_tutorial_pressed() -> void: is_tutorial = not is_tutorial _initialize() func _on_behavior_tree_view_task_selected(_type_name: String, p_script_path: String) -> void: if not p_script_path.is_empty(): var sc: Script = load(p_script_path) code_edit.set_source_code(sc.source_code) code_popup.popup.call_deferred()