[gd_scene load_steps=3 format=2]

[ext_resource path="res://tileset/isotiles.png" type="Texture2D" id=1]

[sub_resource type="OccluderPolygon2D" id=2]
polygon = PackedVector2Array( 0, -100, -67, -68, -66, 0, -1, 33, 66, -2, 64, -67 )

[node name="TilesetEdit" type="Node2D"]

[node name="Base" type="Sprite2D" parent="."]
texture = ExtResource( 1 )
region_enabled = true
region_rect = Rect2( 28, 75, 135, 105 )

[node name="Base2" type="Sprite2D" parent="."]
position = Vector2( 200, 0 )
texture = ExtResource( 1 )
region_enabled = true
region_rect = Rect2( 221, 75, 135, 105 )

[node name="Wall" type="Sprite2D" parent="."]
position = Vector2( 400, 0 )
texture = ExtResource( 1 )
offset = Vector2( 0, -32 )
region_enabled = true
region_rect = Rect2( 28, 220, 140, 140 )

[node name="StaticBody2D" type="StaticBody2D" parent="Wall"]

[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Wall/StaticBody2D"]
polygon = PackedVector2Array( -64, 0, 0, 32, 64, 0, 0, -32 )

[node name="LightOccluder2D" type="LightOccluder2D" parent="Wall"]
visible = false
occluder = SubResource( 2 )

[node name="Column" type="Sprite2D" parent="."]
position = Vector2( 600, 0 )
texture = ExtResource( 1 )
offset = Vector2( 0, -32 )
region_enabled = true
region_rect = Rect2( 259, 241, 55, 95 )

[node name="StaticBody2D" type="StaticBody2D" parent="Column"]

[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Column/StaticBody2D"]
position = Vector2( 2, 0 )
polygon = PackedVector2Array( -27, 7, -14, 20, 16, 20, 28, 4, 24, -12, 10, -22, -10, -22, -24, -12 )

[node name="Door1" type="Sprite2D" parent="."]
position = Vector2( 800, 0 )
texture = ExtResource( 1 )
offset = Vector2( 0, -25 )
region_enabled = true
region_rect = Rect2( 54, 426, 85, 110 )

[node name="StaticBody2D" type="StaticBody2D" parent="Door1"]

[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Door1/StaticBody2D"]
polygon = PackedVector2Array( -24, 24, 40, -8, 24, -16, -40, 16 )


extends CharacterBody2D

const MOTION_SPEED = 160 # Pixels/second.

var last_direction = Vector2(1, 0)

var anim_directions = {
	"idle": [ # list of [animation name, horizontal flip]
		["side_right_idle", false],
		["45front_right_idle", false],
		["front_idle", false],
		["45front_left_idle", false],
		["side_left_idle", false],
		["45back_left_idle", false],
		["back_idle", false],
		["45back_right_idle", false],
	],

	"walk": [
		["side_right_walk", false],
		["45front_right_walk", false],
		["front_walk", false],
		["45front_left_walk", false],
		["side_left_walk", false],
		["45back_left_walk", false],
		["back_walk", false],
		["45back_right_walk", false],
	],
}

func _physics_process(_delta):
	var motion = Vector2()
	motion.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
	motion.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
	motion.y /= 2
	motion = motion.normalized() * MOTION_SPEED
	#warning-ignore:return_value_discarded
	set_velocity(motion)
	move_and_slide()
	var dir = velocity

	if dir.length() > 0:
		last_direction = dir
		update_animation("walk")
	else:
		update_animation("idle")

func update_animation(anim_set):

	var angle = rad_to_deg(last_direction.angle()) + 22.5
	var slice_dir = floor(angle / 45)

	$Sprite2D.play(anim_directions[anim_set][slice_dir][0])
	$Sprite2D.flip_h = anim_directions[anim_set][slice_dir][1]


extends CharacterBody2D

const MOTION_SPEED = 30
const FRICTION_FACTOR = 0.89
const TAN30DEG = tan(deg_to_rad(30))

func _physics_process(_delta: float) -> void:
	var motion := Vector2()
	motion.x = Input.get_axis(&"move_left", &"move_right")
	motion.y = Input.get_axis(&"move_up", &"move_down")
	# Make diagonal movement fit for hexagonal tiles.
	motion.y *= TAN30DEG
	velocity += motion.normalized() * MOTION_SPEED
	# Apply friction.
	velocity *= FRICTION_FACTOR
	move_and_slide()


#!/usr/bin/env bash
# vi:syntax=sh

APP_NAME="learn_to_code"
# This is used while developing to supply the `GODOT_VERSION` variable that is
# normally provided by the CI
local_godot_version=${GODOT_VERSION:-"3.4.4"}

######################################## HELP

help(){
  if [ "$1" == "help" ]; then
    echo "Shows a short and helpful help text"
    exit 0
  fi
  echo ""
  echo "Builder Script for Learn To Code With Godot"
  echo "please use one of the following commands:"
  for i in ${!funcs[@]}; do
    command=${funcs[i]}
    echo "  - ${command//_/:}"
  done
  echo "you can get more info about commands by writing ./run <command> help"
  echo "you dry run most (but not all) commands by setting the \$dry env variable"
  echo ""
}

######################################## CLEAN

clean_web(){
  if [ "$1" == "help" ]; then
    echo "Removes web build"
    exit 0
  fi

  echo "rm -rf \"${dirs[web]}\""
  test "$dry" || rm -rf "${dirs[web]}"
  echo "✓ Removed web files"
}

clean_all(){
  if [ "$1" == "help" ]; then
    echo "Removes all build directories"
    exit 0
  fi

  echo "rm -rf build"
  test "$dry" || rm -rf build
  echo "mkdir -p build"
  test "$dry" || mkdir -p build
  echo "touch build/.gdignore"
  test "$dry" || touch build/.gdignore
  echo "✓ Removed all builds"
}

clean(){
  if [ "$1" == "help" ]; then
    echo "Removes one or all build directories"
    exit 0
  fi

  echo "choose what to clean"
  exit 1
}

######################################## WEB DEV HELPERS

web_server(){
  if [ "$1" == "help" ]; then
    echo "Runs a python webserver in build/web"
    exit 0
  fi
  if ! command -v python3 &> /dev/null; then
      echo ""
      echo "ERROR: Python 3 isn't available. It is required to run the server"
      echo " - in Debian/Ubuntu: sudo apt-get -y install inotify-tools"
      echo " - in Arch/Manjaro: sudo pacman --needed --noconfirm -S python"
      echo " - in Fedora: sudo dnf install python3"
      echo ""
      exit 1
  fi
  echo "python3 -m http.server 8000 --directory \"${dirs[web]}\""
  test "$dry" || python3 -m http.server 8000 --directory "${dirs[web]}"
}

web_watch(){
  if [ "$1" == "help" ]; then
    echo "Watches a directory for changes. Recompiles Godot when a \.gd file changes;"
    echo "Overwrites JS and CSS files in the static directory when they change"
    exit 0
  fi
  if ! command -v inotifywait &> /dev/null; then
      echo ""
      echo "ERROR: inotifywait isn't available. It is required to run the server"
      echo "this command is usually part of the package inotify-tools"
      echo " - in Debian/Ubuntu: sudo apt-get -y install inotify-tools"
      echo " - in Arch/Manjaro: sudo pacman --needed --noconfirm -S inotify-tools"
      echo " - in Fedora: sudo dnf install inotify-tools"
      echo ""
      exit 1
  fi
  last_time_compiled=0
  throttle_compile=10
  last_time_copied=0
  throttle_copy=1
  source_dir=${1:-.}
  full_path=$(readlink -f $source_dir)
  _update_file(){
    if [ "$base_dir" = "html_export" ]; then
      if (($now - $last_time_copied >= $throttle_copy)); then
        if [[ "$filename" == "index_template.html" ]]; then
          prepare_htmltemplate
          changed_file="./html_export/index.html"
          filename="index.html"
        fi
        echo "cp \"$changed_file\" \"${dirs[web]}/$filename\""
        cp "$changed_file" "${dirs[web]}/$filename"
      fi
      last_time_copied=$(date +%s)
    else
      echo "Changes detected in $changed_file, will recompile"
      if (($now - $last_time_compiled >= $throttle_compile)); then
        export_custom "${presets[web]}" "${dirs[web]}/index.html"
      fi
      last_time_compiled=$(date +%s)
    fi
  }
  echo "will watch $full_path"
  inotifywait --monitor --recursive --quiet --event modify,move,create,delete --format '%w%f' "$source_dir" | while read changed_file
  do
    changed_dir=$(dirname "$changed_file")
    base_dir=$(echo "$changed_dir" | cut -d "/" -f2)
    ext=${changed_file##*.}
    now=$(date +%s)
    filename=$(basename "$changed_file")
    ext=$(echo "$ext" |  tr '[:upper:]' '[:lower:]')
    exclude=0
    include=0
    if [[ -d "$$changed_file" ]] || [[ "$filename" =~ ^\. ]] || [[ "$base_dir" == "build" ]]; then
      exclude=1
    fi
    if [[ "$exclude" -eq 0 ]] && [[ $ext =~ gd|tscn|tres|js|css|html ]]; then
      include=1
    fi
    if [[ "$exclude" -eq 0 ]] && [[ "$include" -eq 1 ]]; then
      _update_file
    fi
  done
}

web_debug(){
  if [ "$1" == "help" ]; then
    echo "Exports the web build, starts a python webserver, and watches"
    echo "the directory for changes. Require Python 3 and inotifywait to"
    echo "be in your \$PATH"
    exit 0
  fi
  echo "You need the custom templates. Please make sure to run prepare:local at least once!"
  debug=true
  clean_web
  export_web
  web_server &
  web_watch
}

######################################## EXPORTS

export_custom(){
  if [ "$1" == "help" ]; then
    echo "Exports a Godot build"
    echo "Usage:"
    echo "./run export:any <name> <output_path>"
    echo ""
    echo "for example: ./run export:any \"HTML5\" \"build/web/index.html\""
    echo ""
    echo "<name> needs to be a valid export specified in \`export_presets.cfg\`"
    echo "set the environment variable \$debug to export a debug build"
    exit 0
  fi
  export_name=$1
  output=$2
  dir=$(dirname "$output")
  mkdir -p "$dir"
  prepare_versionfile
  # THERE IS NO DIFFERENCE BETWEEN A DEBUG AND A RELEASE BUILD
  # we export a debug build in all cases, due to https://github.com/GDQuest/learn-gdscript/issues/618
  # we could remove the `debug` keyword, but it is kept both for backwards
  # compatibility, and in case we solve this differently in the future
  # in which case, all that needs to be done is replce `export-debug` with `export`
  if [ -z "$debug" ]; then
    echo "godot --quiet --no-window --export-debug \"$export_name\" \"$output\""
    test "$dry" || godot --quiet --no-window --export-debug "$export_name" "$output"
  else
    echo "godot --quiet --no-window --export-debug \"$export_name\" \"$output\""
    test "$dry" || godot --quiet --no-window --export-debug "$export_name" "$output"
  fi
  echo "✓ Exported \`$export_name\` build to \`$dir\`"
}

export_web(){
  if [ "$1" == "help" ]; then
    echo "Exports the Web build and copies over static files"
    echo "You can specify the following environment variables:"
    echo " - \`\$url\` for the html template"
    echo " - \`\$git_branch\` for deploying to a subfolder (the"
    echo "   \`release\` branch will be blanked, and deployed to"
    echo "   the root subfolder)"
    exit 0
  fi
  prepare_htmltemplate

  export_custom "${presets[web]}" "${dirs[web]}/index.html"

  echo "cp -r html_export/static/* \"${dirs[web]}\""
  test "$dry" || cp -r html_export/static/* "${dirs[web]}"

  echo "✓ Copied web files to ${dirs[web]}"
}

export_osx(){
  if [ "$1" == "help" ]; then
    echo "Exports the OSX build"
    exit 0
  fi
  export_custom "${presets[osx]}" "${dirs[osx]}/$APP_NAME.zip"
}


extends PanelContainer

signal toggled(is_pressed)

const OPTION_FONT := preload("res://ui/theme/fonts/font_text.tres")
const OPTION_SELECTED_FONT := preload("res://ui/theme/fonts/font_text_bold.tres")

var _button_text := ""

onready var _margin_container := $MarginContainer as MarginContainer
onready var _label := $MarginContainer/Label as Label
onready var _group: ButtonGroup = preload("QuizAnswerButtonGroup.tres")
onready var _button := $CheckBox as CheckBox

func _notification(what: int) -> void:
	if what == NOTIFICATION_TRANSLATION_CHANGED:
		_label.text = tr(_button_text)

func setup(text: String, is_multiple_choice: bool) -> void:
	_button_text = text

	if not is_inside_tree():
		yield(self, "ready")

	_label.text = tr(_button_text)
	_label.add_font_override("font", OPTION_FONT)
	_button.toggle_mode = true
	_button.connect("toggled", self, "_on_toggled")
	if not is_multiple_choice:
		_button.group = _group

func get_answer() -> String:
	return _button_text if _button.pressed else ""

func _on_toggled(is_pressed: bool) -> void:
	_label.add_font_override("font", OPTION_SELECTED_FONT if is_pressed else OPTION_FONT)


extends Button

const EDITOR_EXPAND_ICON := preload("res://ui/icons/fullscreen_on.png")
const EDITOR_COLLAPSE_ICON := preload("res://ui/icons/fullscreen_off.png")

func _ready() -> void:
	if OS.has_feature("JavaScript"):
		modulate.a = 0.0
		return

	connect("pressed", self, "_toggle_fullscreen")
	Events.connect("fullscreen_toggled", self, "_toggle_fullscreen")
	_update_icon()

func _toggle_fullscreen() -> void:
	OS.window_fullscreen = not OS.window_fullscreen
	_update_icon()

func _update_icon():
	icon = EDITOR_COLLAPSE_ICON if OS.window_fullscreen else EDITOR_EXPAND_ICON


extends Panel

const TEXTURE_PROCESSING := preload("robot_tutor_running_code.svg")

onready var _texture_rect := $Layout/TextureRect as TextureRect

func _ready() -> void:
	_texture_rect.texture = TEXTURE_PROCESSING


extends Button

func _ready() -> void:
	connect("pressed", get_tree(), "quit")

	if OS.has_feature("JavaScript"):
		queue_free()


# Displays a scene inside a frame.
class_name GameView
extends Control

var paused := false setget set_paused

var _viewport := Viewport.new()

onready var _viewport_container = $ViewportContainer as ViewportContainer
onready var _pause_rect := $PauseRect as ColorRect
onready var _scene_tree := get_tree()

func _ready() -> void:
	_pause_rect.visible = false
	_viewport.name = "Viewport"
	_viewport_container.add_child(_viewport)
	_scene_tree.connect("screen_resized", self, "_on_screen_resized")
	call_deferred("_on_screen_resized")

func toggle_paused() -> void:
	set_paused(not paused)

func set_paused(value: bool) -> void:
	paused = value
	_scene_tree.paused = paused
	_pause_rect.visible = paused

func use_scene(node: Node, viewport_size: Vector2) -> void:
	_viewport.add_child(node)
	_pause_rect.raise()
	_viewport.size = viewport_size

func get_viewport() -> Viewport:
	return _viewport

func _on_screen_resized() -> void:
	_viewport.size = rect_size


tool
class_name Revealer, "./Revealer.svg"
extends Container

const ANIMATION_ICON_DURATION := 0.1
const ANIMATION_REVEAL_DURATION := 0.24

const TOGGLE_OPACITY := 0.65
const TOGGLE_OPACITY_HOVER := 1.0

signal expanded

export var title := "Expand" setget set_title
export var is_expanded := false setget set_is_expanded

export var title_font_color: Color = Color.white setget set_title_font_color
export var title_icon_color: Color = Color.white setget set_title_icon_color
export var title_font: Font setget set_title_font
export var title_panel: StyleBox setget set_title_panel
export var title_panel_expanded: StyleBox setget set_title_panel_expanded

export var content_panel: StyleBox setget set_content_panel
export var content_separation: int = 2 setget set_content_separation

onready var _tweener := $Tween as Tween
onready var _toggle_bar := $ToggleBar as PanelContainer
onready var _toggle_button := $ToggleBar/ToggleCapturer as Button
onready var _toggle_label := $ToggleBar/BarLayout/Label as Label
onready var _toggle_icon_anchor := $ToggleBar/BarLayout/ToggleIcon as Control
onready var _toggle_icon := $ToggleBar/BarLayout/ToggleIcon/Texture as TextureRect

var _content_children := []
var _percent_revealed := 0.0
var _title_style: StyleBox

func _ready() -> void:
	_toggle_label.text = title
	_toggle_button.pressed = is_expanded
	_index_content()
	_update_icon_anchor()
	_update_theme()

	_toggle_bar.modulate.a = TOGGLE_OPACITY

	_toggle_button.connect("mouse_entered", self, "_on_toggle_entered")
	_toggle_button.connect("mouse_exited", self, "_on_toggle_exited")
	_toggle_button.connect("toggled", self, "_on_toggle_pressed")
	_tweener.connect("tween_step", self, "_on_tweener_step")
	_tweener.connect("tween_all_completed", self, "_on_tweener_completed")

	_toggle_content(is_expanded, true)
	_title_style = get_title_panel_style()

func _draw() -> void:
	var title_size := _toggle_bar.rect_size

	if _title_style:
		var title_rect := Rect2()
		title_rect.position = Vector2(0, 0)
		title_rect.size = _toggle_bar.rect_size + _title_style.get_minimum_size()
		draw_style_box(_title_style, title_rect)

		title_size += _title_style.get_minimum_size()

	if content_panel:
		var content_rect := Rect2()
		content_rect.position = Vector2(0, title_size.y)
		content_rect.size = Vector2(rect_size.x, rect_size.y - title_size.y)
		draw_style_box(content_panel, content_rect)

func _notification(what: int) -> void:
	if what == NOTIFICATION_SORT_CHILDREN:
		_resort()

func _index_content() -> void:
	_content_children = []

	for child_node in get_children():
		var control_node := child_node as Control
		if not control_node or control_node == _toggle_bar:
			continue

		_content_children.append(control_node)

func _update_icon_anchor() -> void:
	if not is_inside_tree():
		return

	_toggle_icon_anchor.rect_min_size = _toggle_icon.rect_size
	_toggle_icon.rect_pivot_offset = _toggle_icon.rect_size / 2

func _update_theme() -> void:
	if not is_inside_tree():
		return

	_toggle_label.add_font_override("font", title_font)
	_toggle_label.add_color_override("font_color", title_font_color)
	_toggle_icon.modulate = title_icon_color

func add_child(child_node: Node, legible_unique_name: bool = false) -> void:
	.add_child(child_node, legible_unique_name)

	var control_node := child_node as Control
	if not control_node or control_node == _toggle_bar:
		return

	_content_children.append(control_node)

func remove_child(child_node: Node) -> void:
	if _content_children.has(child_node):
		_content_children.erase(child_node)

	.remove_child(child_node)

func _get_minimum_size() -> Vector2:
	# Title/toggle bar always contributes to the minimal size in full.
	var title_size := Vector2.ZERO
	if is_instance_valid(_toggle_bar):
		title_size = _toggle_bar.get_combined_minimum_size()

	if _title_style:
		title_size += _title_style.get_minimum_size()

	# Content can be partially visible, so we calculate full size, then slice it.
	var content_size := Vector2.ZERO

	var first := true
	for child_node in get_children():
		var control_node := child_node as Control
		if not control_node or not control_node.is_visible_in_tree():
			continue
		if control_node == _toggle_bar:
			continue

		var control_size := control_node.get_combined_minimum_size()
		if content_size.x < control_size.x:
			content_size.x = control_size.x

		content_size.y += control_size.y
		if first:
			first = false
		else:
			content_size.y += content_separation

	if content_panel:
		content_size += content_panel.get_minimum_size()

	content_size.y *= _percent_revealed

	# Combine the two.
	var final_size := title_size
	if final_size.x < content_size.x:
		final_size.x = content_size.x
	final_size.y += content_size.y

	return final_size

func _resort() -> void:
	var content_offset := 0
	var base_width := rect_size.x

	if is_instance_valid(_toggle_bar):
		var bar_position := Vector2.ZERO
		var bar_size := _toggle_bar.rect_min_size
		bar_size.x = base_width

		if _title_style:
			bar_position.x += _title_style.get_margin(MARGIN_LEFT)
			bar_position.y += _title_style.get_margin(MARGIN_TOP)
			bar_size.x -= _title_style.get_margin(MARGIN_LEFT) + _title_style.get_margin(MARGIN_RIGHT)

		fit_child_in_rect(_toggle_bar, Rect2(bar_position, bar_size))
		_update_icon_anchor()

		content_offset = int(_toggle_bar.rect_size.y)
		if _title_style:
			content_offset += int(_title_style.get_margin(MARGIN_TOP) + _title_style.get_margin(MARGIN_BOTTOM))

	var first := true
	for child_node in get_children():
		var control_node := child_node as Control
		if not control_node or not control_node.is_visible_in_tree():
			continue
		if control_node == _toggle_bar:
			continue

		var position := Vector2.ZERO
		var size := control_node.rect_min_size
		size.x = base_width

		if content_panel:
			position.x += content_panel.get_margin(MARGIN_LEFT)
			size.x -= content_panel.get_margin(MARGIN_LEFT) + content_panel.get_margin(MARGIN_RIGHT)

		if first:
			first = false
			if content_panel:
				content_offset += int(content_panel.get_margin(MARGIN_TOP))
		else:
			content_offset += content_separation
		position.y = content_offset

		fit_child_in_rect(control_node, Rect2(position, size))
		content_offset += int(control_node.rect_size.y)

func set_title(value: String) -> void:
	title = value
	if is_inside_tree():
		_toggle_label.text = title

func set_is_expanded(value: bool) -> void:
	if is_expanded == value:
		return

	is_expanded = value
	if is_expanded:
		emit_signal("expanded")

	if is_inside_tree():
		_toggle_button.pressed = is_expanded
		_toggle_content(is_expanded)

func set_title_font_color(value: Color) -> void:
	title_font_color = value
	_update_theme()

func set_title_icon_color(value: Color) -> void:
	title_icon_color = value
	_update_theme()

func set_title_font(value: Font) -> void:
	title_font = value
	_update_theme()
	minimum_size_changed()
	notification(NOTIFICATION_THEME_CHANGED)

func set_title_panel(value: StyleBox) -> void:
	if title_panel == value:
		return

	if title_panel:
		title_panel.disconnect("changed", self, "minimum_size_changed")
		title_panel.disconnect("changed", self, "queue_sort")
		title_panel.disconnect("changed", self, "update")

	title_panel = value
	if title_panel and not title_panel.is_connected("changed", self, "minimum_size_changed"):
		title_panel.connect("changed", self, "minimum_size_changed")
		title_panel.connect("changed", self, "queue_sort")
		title_panel.connect("changed", self, "update")

	minimum_size_changed()
	notification(NOTIFICATION_THEME_CHANGED)


[gd_scene load_steps=3 format=2]

[ext_resource path="res://course/lesson-10-the-game-loop/rotating-and-moving/RotateAndMoveSprite.gd" type="Script" id=1]
[ext_resource path="res://course/common/Robot.tscn" type="PackedScene" id=2]

[node name="RotateAndMoveSprite" type="Node2D"]

[node name="Robot" parent="." instance=ExtResource( 2 )]
script = ExtResource( 1 )

[node name="Camera2D" type="Camera2D" parent="."]
current = true


[gd_scene load_steps=4 format=2]

[ext_resource path="res://ui/theme/fonts/font_title.tres" type="DynamicFont" id=1]
[ext_resource path="res://ui/theme/panel_normal.tres" type="StyleBox" id=2]

[sub_resource type="Theme" id=1]
default_font = ExtResource( 1 )

[node name="VisualFunctionDefinition" type="PanelContainer"]
margin_right = 580.0
margin_bottom = 45.0
rect_min_size = Vector2( 0, 80 )
size_flags_horizontal = 3
custom_styles/panel = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}

[node name="Row" type="HBoxContainer" parent="."]
margin_left = 8.0
margin_top = 24.0
margin_right = 610.0
margin_bottom = 55.0
size_flags_vertical = 4
theme = SubResource( 1 )
custom_constants/separation = 4
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}

[node name="Label6" type="Label" parent="Row"]
margin_right = 602.0
margin_bottom = 31.0
custom_colors/font_color = Color( 0.14902, 0.776471, 0.968627, 1 )
text = "Invalid operands 'String' and 'int' in operator '+'."


extends Panel

signal used
signal restored

const SWORD := preload("res://course/common/inventory/sword.png")
const SHIELD := preload("res://course/common/inventory/shield.png")
const HEALTH := preload("res://course/common/inventory/healing_heart.png")
const GEMS := preload("res://course/common/inventory/gems.png")

const textures = [
	SWORD,
	SHIELD,
	HEALTH,
	GEMS
]

export var texture: Texture setget set_texture, get_texture
export var hide_after_animation := false

onready var anim_player := $AnimationPlayer as AnimationPlayer
onready var texture_rect := $TextureRect as TextureRect
onready var label := $Label as Label

var _animation_backwards := false

func _ready() -> void:
	set_label_index(get_index())
	if texture == null:
		randomize()
		set_random_texture()
	anim_player.connect("animation_finished", self, "_on_animation_finished")

func set_random_texture():
	if get_index() > 0 and get_index() < textures.size():
		# ensure textures appear at least once each in the first loop
		var previous_crate = get_parent().get_child(get_index() - 1)
		if previous_crate and previous_crate.texture:
			var previous_texture_index := textures.find(previous_crate.texture)
			if previous_texture_index > -1:
				var next_index := (previous_texture_index + 1) % textures.size()
				set_texture(textures[next_index])
				return
	randomize_texture()

func randomize_texture():
	set_texture(textures[randi() % textures.size()])

func use() -> void:
	anim_player.play("use")

func reset(speed := 2.0) -> void:
	show()
	if speed == 0:
		anim_player.play("RESET")
		return
	_animation_backwards = true
	anim_player.play("use", -1, -1 * speed, true)

func _on_animation_finished(animation_name: String) -> void:
	if animation_name != "use":
		return
	if _animation_backwards:
		_animation_backwards = false
		emit_signal("restored")
		return
	if hide_after_animation:
		hide()
	emit_signal("used")

func set_texture(new_texture: Texture) -> void:
	texture = new_texture
	if not is_inside_tree():
		yield(self, "ready")
	texture_rect.texture = new_texture

func get_texture() -> Texture:
	return texture

func get_texture_name():
	var path := texture.resource_path
	var filename := path.get_file().get_basename().split("_")
	return PoolStringArray(filename).join(" ")

func set_label_index(index: int) -> void:
	label.text = str(index)


[gd_resource type="Resource" load_steps=3 format=2]

[ext_resource path="res://addons/gdscript-slice-exporter/collections/SceneProperties.gd" type="Script" id=1]
[ext_resource path="res://course/lesson-12-using-variables/fixing_error/FixingError.tscn" type="PackedScene" id=2]

[resource]
script = ExtResource( 1 )
scene = ExtResource( 2 )
viewport_size = Vector2( 1920, 1080 )


When programs stop responding on your computer, it's often due to an infinite loop!"
answer_options = [ "It would draw squares infinitely until the program is terminated", "It would draw 10 squares", "It would draw 20 squares" ]
valid_answers = [ "It would draw squares infinitely until the program is terminated" ]
is_multiple_choice = false
do_shuffle_answers = true

[sub_resource type="Resource" id=21]
script = ExtResource( 2 )
content_id = "res://course/lesson-17-while-loops/content-izKUdOCQ.tres"
title = "When to use while loops"
type = 0
text = "At first, you will not need [code]while[/code] loops often. Even the code we show here has more efficient alternatives.

Also, there's a safer kind of loop, [code]for[/code] loops, which we'll look at in the next lesson.

Yet, [code]while[/code] loops have important intermediate to advanced-level uses, so you at least need to know they exist and how to use them.

We use [code]while[/code] loops every time we need to loop an unknown number of times.

For example, games run in a loop that typically generates sixty images per second until the user closes the game. This is possible thanks to [code]while[/code] loops.

There are other good uses for [code]while[/code] loops:

- Reading and processing a file, like a text document, line by line.
- Processing a constant stream of data, like someone recording audio with a microphone.
- Reading code and converting it into instructions the computer understands.
- Various intermediate to advanced-level algorithms, like finding paths around a map for AI."
visual_element_path = ""
reverse_blocks = false
has_separator = true

[sub_resource type="Resource" id=11]
script = ExtResource( 2 )
content_id = "res://course/lesson-17-while-loops/content-Ib5BnVHA.tres"
title = ""
type = 0
text = "Let's practice some [code]while[/code] loops, as they're useful to know. It's also an excellent opportunity to practice 2D vectors.

Then, we'll move on to the safer [code]for[/code] loops in the following lesson."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=12]
script = ExtResource( 5 )
practice_id = "res://course/lesson-17-while-loops/practice-lkGx0c7D.tres"
title = "Moving to the end of a board"
goal = "Our robot has decided to stand at the top of the board.

Complete the [code]move_to_bottom()[/code] function so the robot moves to the bottom of the board.

The board size is determined by the [code]Vector2[/code] [code]board_size[/code].

The robot's current cell is [code]Vector2(2, 0)[/code].

Make sure to use a [code]while[/code] loop so the function works for any board size."
starting_code = "func move_to_bottom():
	pass"
cursor_line = 0
cursor_column = 0
hints = PoolStringArray( "You\'ll have to alter the [code]y[/code] sub-variable of [code]cell[/code] to move the robot downwards", "To move the robot down, [code]cell.y[/code] needs to increase", "Use [code]while cell.y < board_size.y - 1:[/code]" )
validator_script_path = "while_move/TestPracticeBoardWhile.gd"
script_slice_path = "while_move/PracticeBoardWhile.live-editor/slices/PracticeBoardWhile.move_to_end.slice.tres"
documentation_references = PoolStringArray( "board_size", "cell" )
documentation_resource = ExtResource( 4 )
description = "Use a while loop to have our robot move from the top of the board to the bottom."

[resource]
script = ExtResource( 1 )
title = "Introduction to While Loops"
content_blocks = [ SubResource( 1 ), SubResource( 4 ), SubResource( 22 ), SubResource( 23 ), SubResource( 7 ), SubResource( 20 ), SubResource( 13 ), SubResource( 14 ), SubResource( 15 ), SubResource( 17 ), SubResource( 19 ), SubResource( 18 ), SubResource( 8 ), SubResource( 9 ), SubResource( 10 ), SubResource( 21 ), SubResource( 11 ) ]
practices = [ SubResource( 12 ) ]


[gd_scene load_steps=2 format=2]

[ext_resource path="res://course/lesson-22-functions-return-values/convert-grid-coordinates/ConvertGridCoordinates.gd" type="Script" id=1]

[node name="ConvertGridCoordinates" type="Node2D"]
script = ExtResource( 1 )

[node name="Camera2D" type="Camera2D" parent="."]
current = true


[gd_resource type="Resource" load_steps=25 format=2]

[ext_resource path="res://resources/Lesson.gd" type="Script" id=1]
[ext_resource path="res://resources/ContentBlock.gd" type="Script" id=2]
[ext_resource path="res://resources/QuizChoice.gd" type="Script" id=3]
[ext_resource path="res://course/Documentation.tres" type="Resource" id=4]
[ext_resource path="res://resources/Practice.gd" type="Script" id=5]

[sub_resource type="Resource" id=1]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-jB7tKRHJ.tres"
title = ""
type = 0
text = "We used the [code]range()[/code] function in combination with [code]for[/code] loops."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=2]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-MQ2XNwQM.tres"
title = ""
type = 0
text = ""
visual_element_path = "res://course/lesson-19-creating-arrays/visuals/ExamplePrint.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=3]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-qAYVjotx.tres"
title = ""
type = 0
text = "The [code]range()[/code] function produced an array that the [code]for[/code] keyword could loop over.

We can give [code]for[/code] loops [i]any[/i] array, and they will loop over them just the same.

Instead of using the [code]range()[/code] function, we could manually write the numbers and get the same result."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=4]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-F5HUxZPx.tres"
title = ""
type = 0
text = ""
visual_element_path = "visuals/ExamplePrintArray.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=5]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-Y8VUDpPw.tres"
title = ""
type = 0
text = "For each element inside the array, the [code]for[/code] loop extracts it, stores it in the temporary variable named [code]number[/code], and executes the loop's code once.

Inside the loop, you can access the [code]number[/code] variable, which changes on each [i]iteration[/i].

The code works regardless of the array or where you store it. Often, you will store arrays in variables for easy access."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=6]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-QDwfyqdY.tres"
title = ""
type = 0
text = ""
visual_element_path = "visuals/CodeStoreArrayInVar.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=7]
script = ExtResource( 3 )
quiz_id = "res://course/lesson-20-looping-over-arrays/quiz-n1TxlTtc.tres"
question = "What will this code print?"
content_bbcode = "[code]var numbers = [0, 1, 2]
for number in numbers:
    print(number)
[/code]"
hint = ""
explanation_bbcode = "Compared to previous examples, we store the array in the [code]numbers[/code] variable. Using the [code]numbers[/code] variable in our [code]for[/code] loop allows the computer to access the array of numbers like before.

We have three numbers in the array: [code]0[/code], [code]1[/code], and [code]2[/code].

The loop extracts each of them sequentially and assigns it to the [code]number[/code] temporary variable. As the loop processes each number, the output will print [code]0[/code], then [code]1[/code], then [code]2[/code], each on a separate line."
answer_options = [ "0, 1, and 2", "1, 2, and 3", "0, 0, and 0" ]
valid_answers = [ "0, 1, and 2" ]
is_multiple_choice = false
do_shuffle_answers = true

[sub_resource type="Resource" id=8]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-OexRjTRh.tres"
title = "Making the turtle walk, with a loop"
type = 0
text = "In the previous lesson, you made a turtle walk along a path by writing [code]Vector2[/code] coordinates in an array."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=9]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-ZN4rS5AJ.tres"
title = ""
type = 0
text = ""
visual_element_path = "res://course/lesson-19-creating-arrays/visuals/ExampleTurtleRobotPath.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=13]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-DGt6fUqb.tres"
title = ""
type = 0
text = "It's a [code]for[/code] loop that makes the turtle walk along the path.

The loop works like this: for each coordinate in the array, it moves the turtle once to that cell."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=14]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-TxYej47M.tres"
title = ""
type = 0
text = ""
visual_element_path = "visuals/CodeTurtleMove.tscn"
reverse_blocks = false
has_separator = true

[sub_resource type="Resource" id=10]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-TESv5tT6.tres"
title = ""
type = 0
text = "It's the same principle with unit selection."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=15]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-vGm22rtQ.tres"
title = ""
type = 0
text = ""
visual_element_path = "visuals/ExampleSelectUnits.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=16]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-qE82V6jG.tres"
title = ""
type = 0
text = "For each coordinate in an array named [code]selected_units[/code], we check if there is a unit in that cell. If so, we select it.

In that case, we use an array, a loop, and a condition together."
visual_element_path = ""
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=11]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-hcIb5BnV.tres"
title = ""
type = 0
text = ""
visual_element_path = "visuals/CodeSelectUnits.tscn"
reverse_blocks = false
has_separator = false

[sub_resource type="Resource" id=17]
script = ExtResource( 2 )
content_id = "res://course/lesson-20-looping-over-arrays/content-En91c9ll.tres"
title = ""
type = 0
text = "The code above uses several features you haven't learned yet:

- In a condition, the [code]in[/code] keyword allows you to check if a value exists [i]in[/i] an array.
- The array's [code]append()[/code] function appends a new value at the end of the array.

Notice the use of a period after the [code]selected_units[/code] variable, to call the [code]append()[/code] function. It's because this function exists only on arrays.


[gd_resource type="Resource" load_steps=3 format=2]

[ext_resource path="res://addons/gdscript-slice-exporter/collections/SceneProperties.gd" type="Script" id=1]
[ext_resource path="res://course/lesson-8-defining-variables/define-health-variable/DefineHealthVar.tscn" type="PackedScene" id=2]

[resource]
script = ExtResource( 1 )
scene = ExtResource( 2 )
viewport_size = Vector2( 1920, 1080 )


[gd_resource type="Resource" load_steps=3 format=2]

[ext_resource path="res://course/lesson-14-multiplying/reducing_damage/ReducingDamage.tscn" type="PackedScene" id=1]
[ext_resource path="res://addons/gdscript-slice-exporter/collections/SceneProperties.gd" type="Script" id=2]

[resource]
script = ExtResource( 2 )
scene = ExtResource( 1 )
viewport_size = Vector2( 1920, 1080 )


tool
extends Path2D

const COLOR_GREY := Color("928fb8")

export var graph_size := Vector2.ONE * 250
export var text_x := "x-axis"
export var text_y := "y-axis"
export var axis_increments := 50
export var show_speed := 400

var _line
var _points := []
var _last_point := Vector2.ZERO
# Need a draw offset as the scene is centered in lessons
var _draw_offset := Vector2(-graph_size.x / 2, graph_size.y/2)

onready var _label_x := $LabelX as Label
onready var _label_y := $LabelY as Label

func _ready() -> void:
	update()

	if Engine.editor_hint:
		return

	_label_x.rect_position += _draw_offset
	_label_x.rect_size.x = graph_size.x
	_label_x.text = text_x
	_label_y.rect_position += _draw_offset
	_label_y.rect_size.x = graph_size.y
	_label_y.text = text_y

	_points = curve.tessellate(5, 4)
	_line = Polygon.new(show_speed)
	_line.position = _draw_offset
	_line.line_2d.width = 3
	_line.line_2d.default_color = Color.white
	_line.connect("line_end_moved", self, "_change_sprite_position")
	add_child(_line)

	_last_point = _points[0] + _draw_offset

func run() -> void:
	if _line.is_drawing():
		return

	_line.reset()
	_line.points = _points
	_line.start_draw_animation()

func reset() -> void:
	_line.reset()
	_last_point = Vector2(0, 0)

func _process(_delta: float) -> void:
	if Engine.editor_hint:
		return

	update()

func _change_sprite_position(new_position: Vector2) -> void:
	_last_point = new_position

func _draw() -> void:
	# Don't offset the graph in the editor as drawing curves won't line up
	var draw_offset = Vector2.ZERO if Engine.editor_hint else _draw_offset

	draw_line(Vector2.ZERO + draw_offset, Vector2(graph_size.x, 0) + draw_offset, COLOR_GREY, 4, true)
	draw_line(Vector2.ZERO + draw_offset, Vector2(0, -graph_size.y) + draw_offset, COLOR_GREY, 4, true)

	for i in range(graph_size.x / axis_increments):
		draw_circle(Vector2(axis_increments + i * axis_increments, 0) + draw_offset, 4, Color.white)
	for i in range(graph_size.y / axis_increments):
		draw_circle(-Vector2(0, axis_increments + i * axis_increments) + draw_offset, 4, Color.white)

	if _last_point != Vector2(0, 0):
		draw_circle(_last_point, 4, Color.white)

class Polygon:
	extends Node2D

	var points := PoolVector2Array() setget , get_points
	var draw_speed := 400.0
	var line_2d := Line2D.new()
	var _tween := Tween.new()
	var _current_points := PoolVector2Array()
	var _current_point_index := 0
	var _total_distance := 0.0

	signal animation_finished
	signal line_end_moved(new_coordinates)

	func _init(line_draw_speed := 400.0) -> void:
		add_child(_tween)
		add_child(line_2d)
		_tween.connect("tween_all_completed", self, "next")
		draw_speed = line_draw_speed

	func reset() -> void:
		stop_animation()
		line_2d.clear_points()
		_current_point_index = 0
		_total_distance = 0
		_current_points.resize(0)

	func start_draw_animation() -> void:
		var previous_point := points[0] as Vector2
		for index in range(1, points.size()):
			var p := points[index] as Vector2
			var distance = previous_point.distance_to(p)
			previous_point = p
			_total_distance += distance
		_tween.stop_all()
		next()

	func next() -> void:
		if points.size() - _current_point_index < 2:
			emit_signal("animation_finished")
			return

		var starting_point: Vector2 = points[_current_point_index]
		var destination: Vector2 = points[_current_point_index + 1]
		_current_point_index += 1

		var distance := starting_point.distance_to(destination)
		var animation_duration := distance / draw_speed

		_current_points.append(starting_point)
		line_2d.points = _current_points
		_tween.interpolate_method(
			self, "_animate_point_position", starting_point, destination, animation_duration
		)
		_tween.start()

	func stop_animation() -> void:
		_tween.remove_all()

	func is_drawing() -> bool:
		return _tween.is_active()

	func _animate_point_position(point: Vector2) -> void:
		var new_points := _current_points
		new_points.push_back(point)
		line_2d.points = new_points
		emit_signal("line_end_moved", point + position)

	# Returns the local bounds of the polygon. That is to say, it only takes the
	# point into account in local space, but not the polygon's `position`.
	func get_rect() -> Rect2:
		var top_left := Vector2.ZERO
		var bottom_right := Vector2.ZERO
		for p in points:
			if p.x > bottom_right.x:
				bottom_right.x = p.x
			elif p.x < top_left.x:
				top_left.x = p.x

			if p.y > bottom_right.y:
				bottom_right.y = p.y
			elif p.y < top_left.y:
				top_left.y = p.y
		return Rect2(top_left, bottom_right - top_left)

	func get_center() -> Vector2:
		var rect := get_rect()
		return (rect.position + rect.end) / 2.0 + position

	func get_points() -> PoolVector2Array:
		return points


{
		"patterns":
		[
			["Expected", "type for"],
			["Expected type after"],
		],
		"raw":
		[
			"Expected a type for the class variable.",
			"Expected a type for the class constant.",
			"Expected a type for the variable.",
			"Expected a type for an argument.",
			"Expected a return type for the function.",
			"Expected type for export.",
			"Expected type after 'as'.",
		],
		"code": ErrorCode.MISPLACED_TYPE_IDENTIFIER,
	},
	{
		"patterns":
		[
			["Unknown class"],
			["Couldn't find the subclass"],
			["Couldn't resolve the constant"],
			["Constant isn't a class"],
			["Invalid inheritance (unknown class + subclasses)."],
			["Parser bug: undecidable inheritance."],
			["Couldn't determine inheritance."],
			["Parser bug: unresolved constant."],
			["isn't a valid type (not a script or class), or couldn't be found on base"],
			["isn't declared on base"],
			["isn't declared in the current class"],
			["isn't declared in the current scope"],
			["etter function isn't defined"],  # Yes, etter!
			[
				'Invalid "is" test: the right operand isn\'t a type (neither a native type nor a script).'
			],
			["not present in built-in type"],
			["invalid index", "in constant expression"],
		],
		"raw":
		[
			'Unknown class: "%CLASS_NAME%"',
			"Couldn't find the subclass: %SUBCLASS_NAME%",
			'Couldn\'t resolve the constant "%CONSTANT_NAME%".',
			"Constant isn't a class: %IDENTIFIER%",
			"Invalid inheritance (unknown class + subclasses).",
			"Parser bug: undecidable inheritance.",
			"Couldn't determine inheritance.",
			"Parser bug: unresolved constant.",
			'The identifier "%IDENTIFIER%" isn\'t a valid type (not a script or class), or couldn\'t be found on base "%CLASS_NAME%".',
			'The method "%CALLEE_NAME%" isn\'t declared on base "%TYPE_NAME%".',
			'The method "%CALLEE_NAME%" isn\'t declared in the current class.',
			'The identifier "%IDENTIFIER%" isn\'t declared in the current scope.',
			"The setter function isn't defined.",
			"The getter function isn't defined.",
			'Invalid "is" test: the right operand isn\'t a type (neither a native type nor a script).',
			"Static constant  '%CONSTANT_NAME%' not present in built-in type %BUILTIN_TYPE%.",
			"invalid index in constant expression",
			"invalid index '%INDEX_NAME%' in constant expression",
		],
		"code": ErrorCode.NONEXISTENT_IDENTIFIER,
	},
	{
		"patterns":
		[
			['"yield()" can only be used inside function blocks.'],
			['"self" isn\'t allowed in a static function or constant expression.'],
			[
				'Expected "var", "onready", "remote", "master", "puppet", "sync", "remotesync", "mastersync", "puppetsync".'
			],
			['Expected "var".'],
			['Expected "var" or "func".'],
			['Expected "func".'],
			['Unexpected keyword "continue" outside a loop.'],
			['Unexpected keyword "break" outside a loop.'],
			['"extends" can only be present once per script.'],
			['"extends" must be used before anything else.'],
			['"class_name" is only valid for the main class namespace.'],
			['"class_name" isn\'t allowed in built-in scripts.'],
			['"class_name" can only be present once per script.'],
			['The "tool" keyword can only be present once per script.'],
		],
		"raw":
		[
			'"yield()" can only be used inside function blocks.',
			'"self" isn\'t allowed in a static function or constant expression.',
			'Expected "var", "onready", "remote", "master", "puppet", "sync", "remotesync", "mastersync", "puppetsync".',
			'Expected "var".',
			'Expected "var" or "func".',
			'Expected "func".',
			'Unexpected keyword "continue" outside a loop.',
			'Unexpected keyword "break" outside a loop.',
			'"extends" can only be present once per script.',
			'"extends" must be used before anything else.',
			'"class_name" is only valid for the main class namespace.',
			'"class_name" isn\'t allowed in built-in scripts.',
			'"class_name" can only be present once per script.',
			'The "tool" keyword can only be present once per script.',
		],
		"code": ErrorCode.MISPLACED_KEYWORD,
	},
	{
		"patterns":
		[
			["Expected a constant expression"],
			["constant expression", "or variables", "in a pattern"],
			["Not a constant expression as key"],
		],
		"raw":
		[
			"Expected a constant expression.",
			"Expect constant expression or variables in a pattern",
			"Only constant expression or variables allowed in a pattern",
			"Only constant expressions or variables allowed in a pattern",
			"Not a constant expression as key",
		],
		"code": ErrorCode.EXPECTED_CONSTANT_EXPRESSION,
	},
	{
		"patterns":
		[
			[
				'"extends" constant must be a string.',
			],
			[
				'Invalid "extends" syntax, expected string constant (path) and/or identifier (parent class).',
			],
			[
				'"class_name" syntax: "class_name <UniqueName>"',
			],
			[
				"The class icon must be separated by a comma.",
			],
			[
				'"class" syntax: "class <Name>:" or "class <Name> extends <BaseClass>:"',
			],
		],
		"raw":
		[
			'"extends" constant must be a string.',
			'Invalid "extends" syntax, expected string constant (path) and/or identifier (parent class).',
			'"class_name" syntax: "class_name <UniqueName>"',
			"The class icon must be separated by a comma.",
			'"class" syntax: "class <Name>:" or "class <Name> extends <BaseClass>:"',
		],
		"code": ErrorCode.INVALID_CLASS_DECLARATION,
	},


func assert_file_exists(file_path):
	var disp = 'expected [' + file_path + '] to exist.'
	var f = File.new()
	if(f.file_exists(file_path)):
		_pass(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Asserts that a file should not exist
# ------------------------------------------------------------------------------
func assert_file_does_not_exist(file_path):
	var disp = 'expected [' + file_path + '] to NOT exist'
	var f = File.new()
	if(!f.file_exists(file_path)):
		_pass(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Asserts the specified file is empty
# ------------------------------------------------------------------------------
func assert_file_empty(file_path):
	var disp = 'expected [' + file_path + '] to be empty'
	var f = File.new()
	if(f.file_exists(file_path) and gut.is_file_empty(file_path)):
		_pass(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Asserts the specified file is not empty
# ------------------------------------------------------------------------------
func assert_file_not_empty(file_path):
	var disp = 'expected [' + file_path + '] to contain data'
	if(!gut.is_file_empty(file_path)):
		_pass(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Asserts the object has the specified method
# ------------------------------------------------------------------------------
func assert_has_method(obj, method, text=''):
	var disp = _str(obj) + ' should have method: ' + method
	if(text != ''):
		disp = _str(obj) + ' ' + text
	assert_true(obj.has_method(method), disp)

# Old deprecated method name
func assert_get_set_methods(obj, property, default, set_to):
	_lgr.deprecated('assert_get_set_methods', 'assert_accessors')
	assert_accessors(obj, property, default, set_to)

# ------------------------------------------------------------------------------
# Verifies the object has get and set methods for the property passed in.  The
# property isn't tied to anything, just a name to be appended to the end of
# get_ and set_.  Asserts the get_ and set_ methods exist, if not, it stops there.
# If they exist then it asserts get_ returns the expected default then calls
# set_ and asserts get_ has the value it was set to.
# ------------------------------------------------------------------------------
func assert_accessors(obj, property, default, set_to):
	var fail_count = _summary.failed
	var get_func = 'get_' + property
	var set_func = 'set_' + property

	if(obj.has_method('is_' + property)):
		get_func = 'is_' + property

	assert_has_method(obj, get_func, 'should have getter starting with get_ or is_')
	assert_has_method(obj, set_func)
	# SHORT CIRCUIT
	if(_summary.failed > fail_count):
		return
	assert_eq(obj.call(get_func), default, 'It should have the expected default value.')
	obj.call(set_func, set_to)
	assert_eq(obj.call(get_func), set_to, 'The set value should have been returned.')

# ---------------------------------------------------------------------------
# Property search helper.  Used to retrieve Dictionary of specified property
# from passed object. Returns null if not found.
# If provided, property_usage constrains the type of property returned by
# passing either:
# EDITOR_PROPERTY for properties defined as: export(int) var some_value
# VARIABLE_PROPERTY for properties defined as: var another_value
# ---------------------------------------------------------------------------
func _find_object_property(obj, property_name, property_usage=null):
	var result = null
	var found = false
	var properties = obj.get_property_list()

	while !found and !properties.empty():
		var property = properties.pop_back()
		if property['name'] == property_name:
			if property_usage == null or property['usage'] == property_usage:
				result = property
				found = true
	return result

# ------------------------------------------------------------------------------
# Asserts a class exports a variable.
# ------------------------------------------------------------------------------
func assert_exports(obj, property_name, type):
	var disp = 'expected %s to have editor property [%s]' % [_str(obj), property_name]
	var property = _find_object_property(obj, property_name, EDITOR_PROPERTY)
	if property != null:
		disp += ' of type [%s]. Got type [%s].' % [_strutils.types[type], _strutils.types[property['type']]]
		if property['type'] == type:
			_pass(disp)
		else:
			_fail(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Signal assertion helper.
#
# Verifies that the object and signal are valid for making signal assertions.
# This will fail with specific messages that indicate why they are not valid.
# This returns true/false to indicate if the object and signal are valid.
# ------------------------------------------------------------------------------
func _can_make_signal_assertions(object, signal_name):
	return !(_fail_if_not_watching(object) or _fail_if_does_not_have_signal(object, signal_name))

# ------------------------------------------------------------------------------
# Check if an object is connected to a signal on another object. Returns True
# if it is and false otherwise
# ------------------------------------------------------------------------------
func _is_connected(signaler_obj, connect_to_obj, signal_name, method_name=""):
	if(method_name != ""):
		return signaler_obj.is_connected(signal_name, connect_to_obj, method_name)
	else:
		var connections = signaler_obj.get_signal_connection_list(signal_name)
		for conn in connections:
			if((conn.source == signaler_obj) and (conn.target == connect_to_obj)):
				return true
		return false
# ------------------------------------------------------------------------------
# Watch the signals for an object.  This must be called before you can make
# any assertions about the signals themselves.
# ------------------------------------------------------------------------------
func watch_signals(object):
	_signal_watcher.watch_signals(object)

# ------------------------------------------------------------------------------
# Asserts that an object is connected to a signal on another object
#
# This will fail with specific messages if the target object is not connected
# to the specified signal on the source object.
# ------------------------------------------------------------------------------
func assert_connected(signaler_obj, connect_to_obj, signal_name, method_name=""):
	pass
	var method_disp = ''
	if (method_name != ""):
		method_disp = str(' using method: [', method_name, '] ')
	var disp = str('Expected object ', _str(signaler_obj),\
		' to be connected to signal: [', signal_name, '] on ',\
		_str(connect_to_obj), method_disp)
	if(_is_connected(signaler_obj, connect_to_obj, signal_name, method_name)):
		_pass(disp)
	else:
		_fail(disp)

# ------------------------------------------------------------------------------
# Asserts that an object is not connected to a signal on another object
#
# This will fail with specific messages if the target object is connected
# to the specified signal on the source object.
# ------------------------------------------------------------------------------
func assert_not_connected(signaler_obj, connect_to_obj, signal_name, method_name=""):
	var method_disp = ''
	if (method_name != ""):
		method_disp = str(' using method: [', method_name, '] ')
	var disp = str('Expected object ', _str(signaler_obj),\
		' to not be connected to signal: [', signal_name, '] on ',\
		_str(connect_to_obj), method_disp)
	if(_is_connected(signaler_obj, connect_to_obj, signal_name, method_name)):
		_fail(disp)
	else:
		_pass(disp)

# ------------------------------------------------------------------------------
# Asserts that a signal has been emitted at least once.
#
# This will fail with specific messages if the object is not being watched or
# the object does not have the specified signal
# ------------------------------------------------------------------------------
func assert_signal_emitted(object, signal_name, text=""):
	var disp = str('Expected object ', _str(object), ' to have emitted signal [', signal_name, ']:  ', text)
	if(_can_make_signal_assertions(object, signal_name)):
		if(_signal_watcher.did_emit(object, signal_name)):
			_pass(disp)
		else:
			_fail(_get_fail_msg_including_emitted_signals(disp, object))

# ------------------------------------------------------------------------------
# Asserts that a signal has not been emitted.
#
# This will fail with specific messages if the object is not being watched or
# the object does not have the specified signal
# ------------------------------------------------------------------------------
func assert_signal_not_emitted(object, signal_name, text=""):
	var disp = str('Expected object ', _str(object), ' to NOT emit signal [', signal_name, ']:  ', text)
	if(_can_make_signal_assertions(object, signal_name)):
		if(_signal_watcher.did_emit(object, signal_name)):
			_fail(disp)
		else:
			_pass(disp)

# ------------------------------------------------------------------------------
# Asserts that a signal was fired with the specified parameters.  The expected
# parameters should be passed in as an array.  An optional index can be passed
# when a signal has fired more than once.  The default is to retrieve the most
# recent emission of the signal.
#
# This will fail with specific messages if the object is not being watched or
# the object does not have the specified signal
# ------------------------------------------------------------------------------


extends Object

# ResourceLoader is unreliable when it comes to cache.
# Sub-resources get cached regardless of the argument passed to load function.
# This is a workaround that generates a new file on a fly,
# while making sure that there is no cache record for it.
# This file is then used to load the resource, after which
# the resource takes over the original path.
static func load_fresh(resource_path: String) -> Resource:
	var resource = File.new()
	var error = resource.open(resource_path, File.READ)
	if error != OK:
		printerr("Failed to load resource '" + resource_path + "': Error code " + str(error))
		return null

	var resource_ext = resource_path.get_extension()
	var random_index = randi()
	var intermediate_path = resource_path + "_temp_" + str(random_index) + "." + resource_ext
	while ResourceLoader.has_cached(intermediate_path):
		random_index = randi()
		intermediate_path = resource_path + "_temp_" + str(random_index) + "." + resource_ext

	var intermediate_resource = File.new()
	error = intermediate_resource.open(intermediate_path, File.WRITE)
	if error != OK:
		printerr("Failed to load resource '" + resource_path + "': Error code " + str(error))
		return null

	var resource_content = resource.get_as_text()
	intermediate_resource.store_string(resource_content)
	intermediate_resource.close()
	resource.close()

	var actual_resource = ResourceLoader.load(intermediate_path, "", true)
	actual_resource.take_over_path(resource_path)

	var directory = Directory.new()
	directory.remove(intermediate_path)
	return actual_resource


"Using own name in class file is not allowed (creates a cyclic reference)"
"Can't load global class %IDENTIFIER%, cyclic reference?"
"Identifier not found: %IDENTIFIER%"
"'self' not present in static function!"
"Invalid native class type '%NATIVE_TYPE%'."
"Parser bug: unresolved data type."
"Attempt to call a non-identifier."
"Must use '%IDENTIFIER%' instead of 'self.%IDENTIFIER%' in getter."
"Must use '%IDENTIFIER%' instead of 'self.%IDENTIFIER%' in setter."
"'break'' not within loop"
"'continue' not within loop"
"Cyclic class reference for '%CLASS_NAME%'."
"Parser bug: invalid inheritance."
"Signal '%SIGNAL_NAME%' redefined (in current or parent class)"
"Signal '%SIGNAL_NAME%' redefined (original in native class '%CLASS_NAME%')"
"Expected end of statement (\"%STATEMENT_NAME%\"), got %TOKEN_NAME% (\"%IDENTIFIER%\") instead."
"Expected end of statement (\"%STATEMENT_NAME%\"), got %TOKEN_NAME% instead."
"':' expected at end of line."
"Mixed tabs and spaces in indentation."
"Expression expected"
"Expected ',' or ')'"
"Expected ')' in expression"
"Expected string constant or identifier after '$' or '/'."
"Path expected after $."
"Expected '(' after 'preload'"
"expected string constant as 'preload' argument."
"Can't preload itself (use 'get_script()')."
"Can't preload resource at path: %PATH%"
"Expected ')' after 'preload' path"
"Couldn't fully preload the script, possible cyclic reference or compilation error. Use \"load()\" instead if a cyclic reference is intended."
"\"yield()\" can only be used inside function blocks."
"Expected \"(\" after \"yield\"."
"Expected \",\" after the first argument of \"yield\"."
"Expected \")\" after the second argument of \"yield\"."
"\"self\" isn't allowed in a static function or constant expression."
"Built-in type constant or static function expected after \".\"."
"Static constant  '%CONSTANT_NAME%' not present in built-in type %BUILTIN_TYPE%."
"Using assignment with operation on a variable that was never assigned."
"Misplaced 'not'."
"Expected identifier before 'is' operator"
"Unterminated array"
"expression or ']' expected"
"',' or ']' expected"
"Unterminated dictionary"
"':' expected"
"value expected"
"key or '}' expected"
"',' or '}' expected"
"Duplicate key found in Dictionary literal"
"Expected '(' for parent function call."
"Error parsing expression, misplaced: %TOKEN_NAME%"
"Expected identifier as member"
"Expected ']'"
"Unexpected 'as'."
"Expected type after 'as'."
"Unexpected assign."
"GDScriptParser bug, invalid operator in expression: %EXPRESSION%"
"Unexpected operator"
"Yet another parser bug...."
"Unexpected end of expression..."
"Parser bug..."
"Expected else after ternary if."
"Expected value after ternary else."
"Unexpected two consecutive operators after ternary if."
"Unexpected two consecutive operators after ternary else."
"Unexpected two consecutive operators."
"Invalid argument (#%ARGUMENT_NUMBER%) for '%TYPE_NAME%' constructor."
"Too many arguments for '%TYPE_NAME%' constructor."
"Too few arguments for '%TYPE_NAME%' constructor."
"Invalid arguments for '%TYPE_NAME%' constructor."
"Invalid argument (#%ARGUMENT_NUMBER%) for '%TYPE_NAME%' intrinsic function."
"Too many arguments for '%TYPE_NAME%' intrinsic function."
"Too few arguments for '%TYPE_NAME%' intrinsic function."
"Invalid arguments for '%TYPE_NAME%' intrinsic function."
"invalid index in constant expression"
"invalid index '%INDEX_NAME%' in constant expression"
"Can't assign to constant"
"Can't assign to self."
"Can't assign to an expression"
"Invalid operand for unary operator"
"Invalid operands for operator"
"Invalid export type. Only built-in and native resource types can be exported."
"'..' pattern only allowed at the end of an array pattern"
"Not a valid pattern"
"Expected identifier for binding variable name."
"Binding name of '%BINDING_NAME%' is already declared in this scope."
"'..' pattern only allowed at the end of a dictionary pattern"
"Not a valid key in pattern"
"Not a constant expression as key"
"Expected pattern in dictionary value"
"Not a valid pattern"
"Expect constant expression or variables in a pattern"
"Invalid operator in pattern. Only index (`A.B`) is allowed"
"Only constant expression or variables allowed in a pattern"
"Only constant expressions or variables allowed in a pattern"
"Cannot use bindings with multipattern."
"Expected block in pattern branch"
"The pattern type (%PATTERN_TYPE%) isn't compatible with the type of the value to match (%MATCH_TYPE%)."
"Cannot match an array pattern with a non-array expression."
"Cannot match an dictionary pattern with a non-dictionary expression."
"Multipatterns can't contain bindings"
"Parser bug: missing pattern bind variable."
"Mixed tabs and spaces in indentation."
"Expected \";\" or a line break."
"Expected an identifier for the local variable name."
"Variable \"%VARIABLE_NAME%\" already defined in the scope (at line %LINE_NUMBER%)."
"Expected a type for the variable."
"Expected an indented block after \"if\"."
"Invalid indentation."
"Expected an indented block after \"elif\"."
"Invalid indentation."
"Expected an indented block after \"else\"."
"Expected an indented block after \"while\"."
"Identifier expected after \"for\"."
"\"in\" expected after identifier."
"Expected indented block after \"for\"."
"Unexpected keyword \"continue\" outside a loop."
"Unexpected keyword \"break\" outside a loop."
"Expected indented pattern matching block after \"match\"."
"Expected '(' after assert"
"Wrong number of arguments, expected 1 or 2"
"Unexpected ':=', use '=' instead. Expected end of statement after expression."
"Expected end of statement after expression, got %TOKEN_NAME% instead."
"Mixed tabs and spaces in indentation."
"Unexpected indentation."
"Invalid indentation. Bug?"
"Unindent does not match any outer indentation level."
"Mixed tabs and spaces in indentation."
"\"extends\" can only be present once per script."
"\"extends\" must be used before anything else."
"\"extends\" constant must be a string."
"Invalid \"extends\" syntax, expected string constant (path) and/or identifier (parent class)."
"\"class_name\" is only valid for the main class namespace."
"\"class_name\" isn't allowed in built-in scripts."
"\"class_name\" syntax: \"class_name <UniqueName>\""
"\"class_name\" can only be present once per script."
"Unique global class \"%CLASS_NAME%\" already exists at path: %PATH%"
"The class \"%CLASS_NAME%\" shadows a native class."
"The class \"%CLASS_NAME%\" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error."
"No class icon found at: %PATH%"
"The optional parameter after \"class_name\" must be a string constant file path to an icon."
"The class icon must be separated by a comma."
"The \"tool\" keyword can only be present once per script."
"\"class\" syntax: \"class <Name>:\" or \"class <Name> extends <BaseClass>:\""
"The class \"%CLASS_NAME%\" shadows a native class."
"Can't override name of the unique global class \"%CLASS_NAME%\". It already exists at: %PATH%"
"Another class named \"%CLASS_NAME%\" already exists in this scope (at line %LINE_NUMBER%)."
"A constant named \"%CONSTANT_NAME%\" already exists in the outer class scope (at line%LINE_NUMBER%)."
"A variable named \"%VARIABLE_NAME%\" already exists in the outer class scope (at line %LINE_NUMBER%)."
"Indented block expected."
"Expected \"func\"."
"Expected an identifier after \"func\" (syntax: \"func <identifier>([arguments]):\")."
"The function \"%FUNCTION_NAME%\" already exists in this class (at line %LINE_NUMBER%)."
"The function \"%FUNCTION_NAME%\" already exists in this class (at line %LINE_NUMBER%)."
"Expected \"(\" after the identifier (syntax: \"func <identifier>([arguments]):\" )."
"Expected an identifier for an argument."
"The argument name \"%ARGUMENT_NAME%\" is defined multiple times."
"Expected a type for an argument."
"Default parameter expected."
"default argument must be constant"
"Expected \",\" or \")\"."
"The constructor cannot be static."
"Expected \"(\" for parent constructor arguments."
"Expected \",\" or \")\"."
"Parent constructor call found for a class without inheritance."
"Expected a return type for the function."
"Indented block expected after declaration of \"%FUNCTION_NAME%\" function."
"Expected an identifier after \"signal\"."
"The signal \"%SIGNAL_NAME%\" already exists in this class (at line: %LINE_NUMBER%)."
"Expected an identifier in a \"signal\" argument."
"Expected \",\" or \")\" after a \"signal\" parameter identifier."
"Can't export null type."
"Can't export raw object type."
"Expected \",\" in the bit flags hint."
"Expected a string constant in the named bit flags hint."
"Expected \")\" or \",\" in the named bit flags hint."
"Expected \")\" in the layers 2D render hint."
"Expected \")\" in the layers 2D physics hint."
"Expected \")\" in the layers 3D render hint."
"Expected \")\" in the layers 3D physics hint."


# Compiling for web

Because I need this often, here's a one-liner to compile for web:

```sh
rm -rf build/web && \
  mkdir -p build/web && \
  godot -v --export "HTML5" build/web/index.html && \
  cp -r html_export/static/* build/web && \
  cd build/web && \
  python -m http.server 8000 && \
  cd -
```

(This assumes `python` and `godot` are available on `$PATH`)


# Global signal bus registered as an auto-loaded node. Allows us to emit events
# different nodes listen to without having to bubble signals through long chains
# of UI nodes.
extends Node

signal lesson_started(lesson)
signal lesson_completed(lesson)
signal lesson_reading_block(block, previous_blocks)
signal quiz_completed(quiz)
signal practice_started(practice)
signal practice_run_completed()
signal practice_completed(practice)
signal practice_next_requested(practice)
signal practice_previous_requested(practice)
signal practice_requested(practice)
signal course_completed(course)

signal settings_requested
signal report_form_requested
signal fullscreen_toggled
signal font_size_scale_changed(new_font_size)


extends KinematicBody2D

export var jump_speed := 200
export var jump_max_height := 100.0
export var gravity := 4000

var is_walking := false

onready var animated_sprite := $AnimatedSprite as AnimatedSprite

var _velocity_y = 0.0
var _jump_starting_point := 0.0

func _ready() -> void:
	add_to_group("Player")

func _physics_process(delta: float) -> void:
	if Input.is_action_pressed("jump"):
		# EXPORT jump
		if is_on_floor():
			_jump_starting_point = position.y
			_velocity_y -= jump_speed
		else:
			var jump_height := _jump_starting_point - position.y
			var is_going_up: bool = _velocity_y < 0
			var is_under_apex: bool = jump_height < jump_max_height
			if is_going_up and is_under_apex:
				_velocity_y -= jump_speed
		# /EXPORT jump

	if Input.is_action_just_pressed("jump"):
		animated_sprite.play("jump")
	elif is_on_floor() and is_walking:
		animated_sprite.play("walk")

	_velocity_y += gravity * delta

	var velocity := Vector2(0, _velocity_y)
	_velocity_y = move_and_slide(velocity, Vector2.UP).y


[gd_scene load_steps=2 format=2]

[ext_resource path="res://game_demos/run_and_jump/Obstacle.gd" type="Script" id=1]

[node name="Area2D" type="Area2D"]
script = ExtResource( 1 )

[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2( 0, -0.203634 )

[node name="Sprite" type="Sprite" parent="."]


[gd_resource type="Resource" load_steps=3 format=2]

[ext_resource path="res://addons/gdscript-slice-exporter/collections/ScriptProperties.gd" type="Script" id=1]
[ext_resource path="res://game_demos/run_and_jump/Player.gd" type="Script" id=2]

[resource]
script = ExtResource( 1 )
nodes_paths = [ NodePath("Level/Player") ]
script_file = ExtResource( 2 )
original_script = "extends KinematicBody2D

export var jump_speed := 200
export var jump_max_height := 100.0
export var gravity := 4000

var is_walking := false

onready var animated_sprite := $AnimatedSprite as AnimatedSprite

var _velocity_y = 0.0
var _jump_starting_point := 0.0

func _ready() -> void:
	add_to_group(\"Player\")

func _physics_process(delta: float) -> void:
	if Input.is_action_pressed(\"jump\"):
		# EXPORT jump
		if is_on_floor():
			_jump_starting_point = position.y
			_velocity_y -= jump_speed
		else:
			var jump_height := _jump_starting_point - position.y
			var is_going_up: bool = _velocity_y < 0
			var is_under_apex: bool = jump_height < jump_max_height
			if is_going_up and is_under_apex:
				_velocity_y -= jump_speed
		# /EXPORT jump

	if Input.is_action_just_pressed(\"jump\"):
		animated_sprite.play(\"jump\")
	elif is_on_floor() and is_walking:
		animated_sprite.play(\"walk\")

	_velocity_y += gravity * delta

	var velocity := Vector2(0, _velocity_y)
	_velocity_y = move_and_slide(velocity, Vector2.UP).y
"


- Fix editor crash when unsupported `Resource` is dropped in scene ([GH-89126](https://github.com/godotengine/godot/pull/89126)).
- Update lock and group button state when selection changed in CanvasItemEditor ([GH-89132](https://github.com/godotengine/godot/pull/89132)).
- Expose TileMapLayer ([GH-89179](https://github.com/godotengine/godot/pull/89179)).
- Make CanvasItem's "drawing outside of NOTIFICATION_DRAW" error a macro ([GH-89298](https://github.com/godotengine/godot/pull/89298)).
- Fix center view button appears outside the GenericTilePolygonEditor ([GH-89434](https://github.com/godotengine/godot/pull/89434)).
- Fix call queue problems when loading TileSet ([GH-89493](https://github.com/godotengine/godot/pull/89493)).
- Avoid passing zoom scale for ParallaxLayer mirror ([GH-89572](https://github.com/godotengine/godot/pull/89572)).
- Account for scale in scroll logic for `Parallax2D` ([GH-89627](https://github.com/godotengine/godot/pull/89627)).
- Fix Path2D's add new point's UndoRedo bug ([GH-89874](https://github.com/godotengine/godot/pull/89874)).
- TileSet: Expose `TileData.is_valid_terrain_peering_bit` ([GH-89911](https://github.com/godotengine/godot/pull/89911)).
- Replace Clipper1 library by Clipper2 library ([GH-90153](https://github.com/godotengine/godot/pull/90153)).
- Ensure `Camera2D.position_smoothing_speed` is non-negative ([GH-90167](https://github.com/godotengine/godot/pull/90167)).
- Correctly update TileMapLayer highlighting when disabling it ([GH-90204](https://github.com/godotengine/godot/pull/90204)).
- Fix "no cached rect" errors in TileMapLayer editor ([GH-90207](https://github.com/godotengine/godot/pull/90207)).
- Fix strange visual bug with camera and external change ([GH-90317](https://github.com/godotengine/godot/pull/90317)).
- TileSet: Don't save angular and linear physics velocities if they have their default values ([GH-90384](https://github.com/godotengine/godot/pull/90384)).
- Fix selection rect drawing in `TileSet` editor when create/remove tiles with separation ([GH-90709](https://github.com/godotengine/godot/pull/90709)).
- Fix rendering transform of Y-sorted branch-root ([GH-90749](https://github.com/godotengine/godot/pull/90749)).
- Fix TileMap enabling Y-sort on child nodes ([GH-90752](https://github.com/godotengine/godot/pull/90752)).
- Reuse single erase pattern in tile editor ([GH-90817](https://github.com/godotengine/godot/pull/90817)).
- Editor: Fix crash when editing TileMap ([GH-90849](https://github.com/godotengine/godot/pull/90849)).
- Don't store TileMapLayer data if empty ([GH-90907](https://github.com/godotengine/godot/pull/90907)).
- Fix typo in Camera2D node's anchor mode ([GH-91076](https://github.com/godotengine/godot/pull/91076)).
- Fix Parallax2D physics interpolation ([GH-91706](https://github.com/godotengine/godot/pull/91706)).
- Add support for AtlasTexture in `draw_polygon()` ([GH-91724](https://github.com/godotengine/godot/pull/91724)).
- Fix 2d editor selection persisting after application loses focus ([GH-91732](https://github.com/godotengine/godot/pull/91732)).
- Fix TileMap selection pattern being needlessly reorganized ([GH-91905](https://github.com/godotengine/godot/pull/91905)).
- Allow selecting TileMapLayers by clicking them ([GH-92016](https://github.com/godotengine/godot/pull/92016)).
- Fix tile polygon grid not covering whole tile ([GH-92171](https://github.com/godotengine/godot/pull/92171)).
- Remove some TileMap dependencies from TileMapLayer ([GH-92194](https://github.com/godotengine/godot/pull/92194)).
- Display custom data name instead of indices in TileData inspector ([GH-92322](https://github.com/godotengine/godot/pull/92322)).
- Fix TileSet caching wrong collision layer shapes for shapes comprising several polygons ([GH-92373](https://github.com/godotengine/godot/pull/92373)).
- Optimize usage of position icon in 2D editor ([GH-92392](https://github.com/godotengine/godot/pull/92392)).
- Fix incorrect cull boundary for scaled and repeated Parallax2D children ([GH-92763](https://github.com/godotengine/godot/pull/92763)).
- Implement X-draw-order switch in TileMapLayer ([GH-92787](https://github.com/godotengine/godot/pull/92787)).
- Make texture filtering in TileAtlasView consistent ([GH-92926](https://github.com/godotengine/godot/pull/92926)).
- Update Camera2D gizmos when screen size changes ([GH-92992](https://github.com/godotengine/godot/pull/92992)).
- Fix tile polygon editor grid and missing update ([GH-93047](https://github.com/godotengine/godot/pull/93047)).
- Allow canceling actions in Path2D editor ([GH-93087](https://github.com/godotengine/godot/pull/93087)).
- Store TileMapLayer selection in scene's history ([GH-93475](https://github.com/godotengine/godot/pull/93475)).
- Fix snapping lines don't disappearing after drag anchors ([GH-93527](https://github.com/godotengine/godot/pull/93527)).
- Fix TileSet property painter popup showing clear color ([GH-93609](https://github.com/godotengine/godot/pull/93609)).
- Prevent selecting when a CanvasItem is selected ([GH-93671](https://github.com/godotengine/godot/pull/93671)).
- Fix some TileMapLayer editing problems ([GH-93747](https://github.com/godotengine/godot/pull/93747)).
- Fix RMB erasing of tiles ([GH-93850](https://github.com/godotengine/godot/pull/93850)).
- Fix crash in tile physics editor ([GH-93971](https://github.com/godotengine/godot/pull/93971)).
- Fix crash in the TileMapLayer editor when using editable children ([GH-93974](https://github.com/godotengine/godot/pull/93974)).
- Add pixel snap for `Parallax2D` ([GH-94014](https://github.com/godotengine/godot/pull/94014)).
- Remove unused assignment in Parallax2D ([GH-94035](https://github.com/godotengine/godot/pull/94035)).
- Fixes/node configuration warning dialog text ([GH-94147](https://github.com/godotengine/godot/pull/94147)).
- Make sure that selected tile source is remembered ([GH-94356](https://github.com/godotengine/godot/pull/94356)).


- Fix `Path3D` tilt gizmo raycasting against local plane ([GH-91109](https://github.com/godotengine/godot/pull/91109)).
- Simplify the GPUParticles3D editor gizmo to improve readability ([GH-91226](https://github.com/godotengine/godot/pull/91226)).
- Add MultiNodeEdit support to MeshInstance3D editor ([GH-91620](https://github.com/godotengine/godot/pull/91620)).
- Disallow selection of ownerless nodes ([GH-92188](https://github.com/godotengine/godot/pull/92188)).
- Fix bounding box glitch ([GH-92275](https://github.com/godotengine/godot/pull/92275)).
- Fix null `debug_shape` being updated when `CollisionObject3D`'s transform changed ([GH-92302](https://github.com/godotengine/godot/pull/92302)).
- Make CSG shape dirty after changing Snap ([GH-93242](https://github.com/godotengine/godot/pull/93242)).
- Improve `SurfaceTool::generate_tangents` UV error message ([GH-93326](https://github.com/godotengine/godot/pull/93326)).
- Fix invalid inheritance of `OccluderInstance3D` ([GH-93354](https://github.com/godotengine/godot/pull/93354)).
- Fix Subviewport keeps using removed Camera3D child ([GH-93377](https://github.com/godotengine/godot/pull/93377)).
- Fix crash in Node3DEditorViewport selecting on empty scene ([GH-93404](https://github.com/godotengine/godot/pull/93404)).
- Improve viewport rotation gizmo drawing ([GH-93639](https://github.com/godotengine/godot/pull/93639)).
- Add alternative shortcut for Align Transform to View in the 3D editor ([GH-93675](https://github.com/godotengine/godot/pull/93675)).
- Fix "selectable nodes at position clicked" feature in 3D editor ([GH-94387](https://github.com/godotengine/godot/pull/94387)).


revent race condition on initial breakpoints from DAP ([GH-84895](https://github.com/godotengine/godot/pull/84895)).
- Do not bother with line colors if `line_number_gutter` is not yet calculated ([GH-84907](https://github.com/godotengine/godot/pull/84907)).
- Improve search/replace bar behavior ([GH-84932](https://github.com/godotengine/godot/pull/84932)).
- Add an option to center children around the new parent when reparenting ([GH-84995](https://github.com/godotengine/godot/pull/84995)).
- Fix issue with 3D scene drag and drop preview node ([GH-85087](https://github.com/godotengine/godot/pull/85087)).
- Don't abort loading when `ext_resource` is missing ([GH-85159](https://github.com/godotengine/godot/pull/85159)).
- Add further details on properties returning `Packed*Array` ([GH-85207](https://github.com/godotengine/godot/pull/85207)).
- Don't insert newline while saving ([GH-85228](https://github.com/godotengine/godot/pull/85228)).
- Use the flat button style on switches in scene and node docks ([GH-85236](https://github.com/godotengine/godot/pull/85236)).
- Use `SafeNumeric` to protect `max_index` in ImportThreadData ([GH-85295](https://github.com/godotengine/godot/pull/85295)).
- Make copy & paste params skip `resource_path` ([GH-85362](https://github.com/godotengine/godot/pull/85362)).
- Improve SceneTreeEditor usability ([GH-85386](https://github.com/godotengine/godot/pull/85386)).
- Make it possible to show code hint and code completion at the same time ([GH-85436](https://github.com/godotengine/godot/pull/85436)).
- Improve Control hiding in PluginConfigDialog ([GH-85470](https://github.com/godotengine/godot/pull/85470)).
- Update NodePaths only in built-in resources ([GH-85502](https://github.com/godotengine/godot/pull/85502)).
- Expose `Node.is_part_of_edited_scene()` ([GH-85515](https://github.com/godotengine/godot/pull/85515)).
- Fix for Cmd-B shortcut conflict on macOS ([GH-85527](https://github.com/godotengine/godot/pull/85527)).
- Fix SnapGrid is almost invisible in light theme ([GH-85585](https://github.com/godotengine/godot/pull/85585)).
- Defer creating new editor process in "Quit to Project List" just like in "Reload Current Project" ([GH-85654](https://github.com/godotengine/godot/pull/85654)).
- Mark `gui/theme/custom` and `gui/theme/custom_font` as basic properties ([GH-85660](https://github.com/godotengine/godot/pull/85660)).
- Add separate feature tags for editor runtime ([GH-85678](https://github.com/godotengine/godot/pull/85678)).
- Hide Node dock successfully on undo/redo and deletion ([GH-85686](https://github.com/godotengine/godot/pull/85686)).
- Fix theme application in various editor dialogs ([GH-85745](https://github.com/godotengine/godot/pull/85745)).
- Improve engine startup/shutdown benchmarks ([GH-85885](https://github.com/godotengine/godot/pull/85885)).
- Add a editor FileSystem dock action to open a terminal in selected folder ([GH-85923](https://github.com/godotengine/godot/pull/85923)).
- Stop the searching of `find in files` in folders that have `.gdignore` ([GH-85943](https://github.com/godotengine/godot/pull/85943)).
- Tweak Help menu icons for better visibility of commonly used items ([GH-85978](https://github.com/godotengine/godot/pull/85978)).
- Disable Add button when theme item name is empty ([GH-86044](https://github.com/godotengine/godot/pull/86044)).
- Fix "Class name cannot be empty" error when sorting no import files sort by type ([GH-86064](https://github.com/godotengine/godot/pull/86064)).
- Stop using `RTR()` inside the "editor" folder ([GH-86143](https://github.com/godotengine/godot/pull/86143)).
- Properly select the newly duplicated file ([GH-86164](https://github.com/godotengine/godot/pull/86164)).
- Fix file disappearing when renaming dependencies ([GH-86177](https://github.com/godotengine/godot/pull/86177)).
- Fix duplicating multiple nodes at different depths in `SceneTreeDock` ([GH-86211](https://github.com/godotengine/godot/pull/86211)).
- Add option to add built-in strings in the POT generation ([GH-86222](https://github.com/godotengine/godot/pull/86222)).
- Use `set_value_no_signal` in editor property code ([GH-86266](https://github.com/godotengine/godot/pull/86266)).
- Allow node visibility to work with custom user-provided node types ([GH-86268](https://github.com/godotengine/godot/pull/86268)).
- Optimize scanning routines in the project manager ([GH-86271](https://github.com/godotengine/godot/pull/86271)).
- Fix scene dock search losing its default tooltip after typing an invalid filter ([GH-86278](https://github.com/godotengine/godot/pull/86278)).
- Add input action name to window title in input map editor ([GH-86280](https://github.com/godotengine/godot/pull/86280)).
- Add hover highlight to main editor buttons ([GH-86378](https://github.com/godotengine/godot/pull/86378)).
- Strip edges of editor layout names ([GH-86386](https://github.com/godotengine/godot/pull/86386)).
- Clear sub-resources list when no sub-resource exists ([GH-86388](https://github.com/godotengine/godot/pull/86388)).
- Fix internal profiling button being visible when disabled in settings ([GH-86398](https://github.com/godotengine/godot/pull/86398)).
- Improve action name for ungroup button in Scene dock ([GH-86431](https://github.com/godotengine/godot/pull/86431)).
- Improve performance of the 'Create New Node' dialog ([GH-86447](https://github.com/godotengine/godot/pull/86447)).
- Use ObjectID's instead of node pointers to track scene groups to prevent crash ([GH-86462](https://github.com/godotengine/godot/pull/86462)).
- Store horizontal and vertical split offsets separately in FileSystem dock ([GH-86476](https://github.com/godotengine/godot/pull/86476)).
- Fix scene parser reading non-built-in scripts for POT generation ([GH-86479](https://github.com/godotengine/godot/pull/86479)).
- Improve `EditorDirDialog` ([GH-86486](https://github.com/godotengine/godot/pull/86486)).
- Improve Path2D editing ([GH-86542](https://github.com/godotengine/godot/pull/86542)).
- Project upgrade tool: Better space handling in "export" ([GH-86598](https://github.com/godotengine/godot/pull/86598)).
- Don't update tree on deselect ([GH-86605](https://github.com/godotengine/godot/pull/86605)).
- Add option for editor to follow system theme and accent colors ([GH-86610](https://github.com/godotengine/godot/pull/86610)).
- Fix possible crash (use after free) in ScriptTextEditor ([GH-86633](https://github.com/godotengine/godot/pull/86633)).
- Improved synchronization of Transforms during live debug sessions ([GH-86659](https://github.com/godotengine/godot/pull/86659)).
- Automatically add path to built-in shaders ([GH-86668](https://github.com/godotengine/godot/pull/86668)).
- Stop escaping `'` on POT generation ([GH-86669](https://github.com/godotengine/godot/pull/86669)).
- I


ix data race against `EditorFileSystem.scanning_changes_done` ([GH-88124](https://github.com/godotengine/godot/pull/88124)).
- Use `SafeFlag` for `EditorHTTPServer.server_quit` ([GH-88155](https://github.com/godotengine/godot/pull/88155)).
- Use highlighted version of the tab icon in the bottom file system dock ([GH-88191](https://github.com/godotengine/godot/pull/88191)).
- Asset Library author link, description popup size and UX details ([GH-88229](https://github.com/godotengine/godot/pull/88229)).
- Fix update property for dictionaries so that it updates only what is necessary ([GH-88231](https://github.com/godotengine/godot/pull/88231)).
- Fix an edge case bug in drag-and-drop Node3D spawning ([GH-88269](https://github.com/godotengine/godot/pull/88269)).
- Rework how script is edited when clicking icon ([GH-88288](https://github.com/godotengine/godot/pull/88288)).
- Add support for search shortcut to signal connection dialog ([GH-88317](https://github.com/godotengine/godot/pull/88317)).
- Fix skipping normal category followed by custom one ([GH-88318](https://github.com/godotengine/godot/pull/88318)).
- Focus value editor on type change in Dictionary and Array editors ([GH-88322](https://github.com/godotengine/godot/pull/88322)).
- Add const lvalue ref to `editor/*` container parameters ([GH-88368](https://github.com/godotengine/godot/pull/88368)).
- Improve look of Donors list in Editor's About ([GH-88370](https://github.com/godotengine/godot/pull/88370)).
- Move EditorNode setting registration so they are in class reference ([GH-88380](https://github.com/godotengine/godot/pull/88380)).
- Fix Skeleton3D insert key tooltips ([GH-88386](https://github.com/godotengine/godot/pull/88386)).
- Fix resource previews not created in compatibility rendering ([GH-88391](https://github.com/godotengine/godot/pull/88391)).
- Show keywords in `EditorHelpSearch` ([GH-88450](https://github.com/godotengine/godot/pull/88450)).
- Hide Search Results by default and show it on first search ([GH-88465](https://github.com/godotengine/godot/pull/88465)).
- Improve merging of docs on generation ([GH-88514](https://github.com/godotengine/godot/pull/88514)).
- Add separate program case for Godot Resources ([GH-88523](https://github.com/godotengine/godot/pull/88523)).
- Fix crash on documentation generation on macOS ([GH-88545](https://github.com/godotengine/godot/pull/88545)).
- Fix crash on changing resource type of dictionary new key/value while editing the resource ([GH-88572](https://github.com/godotengine/godot/pull/88572)).
- Add `android` to auto-generated `.gitignore` ([GH-88591](https://github.com/godotengine/godot/pull/88591)).
- Remove error when property is outside inspector ([GH-88597](https://github.com/godotengine/godot/pull/88597)).
- Improve user experience for VCS metadata generator menu button to mitigate accidentally overriding ([GH-88609](https://github.com/godotengine/godot/pull/88609)).
- Add a `h_separation` between icons in `CheckButton`/`CheckBox` ([GH-88615](https://github.com/godotengine/godot/pull/88615)).
- Dictionary Editor: Keep the type of the last added item ([GH-88636](https://github.com/godotengine/godot/pull/88636)).
- Fix editor hover style margins when Draw Extra Borders is enabled ([GH-88652](https://github.com/godotengine/godot/pull/88652)).
- Add tokenized search support to Quick Open dialog and FileSystem filter ([GH-88660](https://github.com/godotengine/godot/pull/88660)).
- Remove some redundant method calls from ScriptEditor ([GH-88675](https://github.com/godotengine/godot/pull/88675)).
- Improve Run Instances Dialog ([GH-88685](https://github.com/godotengine/godot/pull/88685)).
- Fix undo/redo behavior of ColorPicker and add ability to cancel/confirm color selection ([GH-88690](https://github.com/godotengine/godot/pull/88690)).
- Fix leak of scene used for customization during export ([GH-88726](https://github.com/godotengine/godot/pull/88726)).
- Fix removing element in array inspector not changing page when emptying page ([GH-88731](https://github.com/godotengine/godot/pull/88731)).
- Fix the text editor theme not being applied on editor start ([GH-88742](https://github.com/godotengine/godot/pull/88742)).
- Inspector `(N changes)` indicator propagates upwards ([GH-88814](https://github.com/godotengine/godot/pull/88814)).
- Fix toggling a plugin makes its name black ([GH-88817](https://github.com/godotengine/godot/pull/88817)).
- Add create folder icon ([GH-88825](https://github.com/godotengine/godot/pull/88825)).
- Add browse folder and browse file icons ([GH-88827](https://github.com/godotengine/godot/pull/88827)).
- Save scene and global checkbox options of History window ([GH-88833](https://github.com/godotengine/godot/pull/88833)).
- Fix paste Value can empty a dictionary depending on right-click location ([GH-88849](https://github.com/godotengine/godot/pull/88849)).
- Remove redundant space after enum/flags word in editor docs ([GH-88911](https://github.com/godotengine/godot/pull/88911)).
- Modify shortcut for closing scene on macOS only ([GH-88913](https://github.com/godotengine/godot/pull/88913)).
- Allow docks to be closed and opened ([GH-89017](https://github.com/godotengine/godot/pull/89017)).
- Add autocompletion for EditorSettings' methods ([GH-89043](https://github.com/godotengine/godot/pull/89043)).
- Use `META_UNDERLINE_ON_HOVER` in built-in class reference ([GH-89049](https://github.com/godotengine/godot/pull/89049)).
- Display deprecated/experimental messages in tooltips ([GH-89058](https://github.com/godotengine/godot/pull/89058)).
- Remember search text in Find/Replace in Files dialog ([GH-89085](https://github.com/godotengine/godot/pull/89085)).
- Improve how Project & Editor Settings look in built-in docs ([GH-89086](https://github.com/godotengine/godot/pull/89086)).
- Do not attempt to set cursor shape in headless mode ([GH-89099](https://github.com/godotengine/godot/pull/89099)).
- Add missing `variablesReference` field to DAP `evaluate` request ([GH-89110](https://github.com/godotengine/godot/pull/89110)).
- Fix undo action names for node replacement ([GH-89121](https://github.com/godotengine/godot/pull/89121)).
- Fix documentation of localization related editor settings ([GH-89135](https://github.com/godotengine/godot/pull/89135)).
- Change Editable Children menu action to be keyboard shortcuttable ([GH-89142](https://github.com/godotengine/godot/pull/89142)).
- Add Enter shortcut to add a shader global in the editor ([GH-89184](https://github.com/godotengine/godot/pull/89184)).
- Fix AssetLibrary not going online when clicking button ([GH-89200](https://github.com/godotengine/godot/pull/89200)).
- Fix wrong undo-redo action when dropping files containing circular dependencies ([GH-89204](https://github.com/godotengine/godot/pull/89204)).
- Don't refresh mirrors for development builds in editor export template manager ([GH-89236](https://github.com/godotengine/godot/pull/89236)).
- A


iversal: Fix texture decompression ([GH-88676](https://github.com/godotengine/godot/pull/88676)).
- Properly calculate binormal when creating SurfaceTool from arrays ([GH-88725](https://github.com/godotengine/godot/pull/88725)).
- Multiple fixes for compressed meshes ([GH-88738](https://github.com/godotengine/godot/pull/88738)).
- Allow preserving the initial bone pose in rest fixer ([GH-88821](https://github.com/godotengine/godot/pull/88821)).
- EditorFileSystem: Add verbose print for file being (re)imported ([GH-88904](https://github.com/godotengine/godot/pull/88904)).
- Fix wrong indexing when generating dummy tangents in GLTF import ([GH-88931](https://github.com/godotengine/godot/pull/88931)).
- Only store vertices referenced by the indices per surface in the glTF importer ([GH-89418](https://github.com/godotengine/godot/pull/89418)).
- BasisUniversal: Fix artifacts on images with resolutions not divisible by 4 ([GH-89426](https://github.com/godotengine/godot/pull/89426)).
- Default to trimesh for generated collision shapes in Advanced Import Settings ([GH-89461](https://github.com/godotengine/godot/pull/89461)).
- Fix method bindings in FBXDocument by making them virtual in GLTFDocument ([GH-89532](https://github.com/godotengine/godot/pull/89532)).
- GLTF: Extract converting hull points to mesh to a helper function ([GH-89542](https://github.com/godotengine/godot/pull/89542)).
- Fix crash when columns are skipped in CSV translation ([GH-89573](https://github.com/godotengine/godot/pull/89573)).
- Fix import and saving related crashes ([GH-89780](https://github.com/godotengine/godot/pull/89780)).
- Fix 2 bugs with scale of position tracks in rest fixer ([GH-90065](https://github.com/godotengine/godot/pull/90065)).
- GLTF export: Propagate `property_list_changed` from extensions ([GH-90225](https://github.com/godotengine/godot/pull/90225)).
- Separate Shape3D resource logic in GLTFPhysicsShape ([GH-90230](https://github.com/godotengine/godot/pull/90230)).
- Fix `GLTFDocument.append_from_scene` crash on null node ([GH-90505](https://github.com/godotengine/godot/pull/90505)).
- Fix errors when re-importing 3D asset files ([GH-90531](https://github.com/godotengine/godot/pull/90531)).
- FBX: Fix material colors ([GH-90554](https://github.com/godotengine/godot/pull/90554)).
- Rename accessor GLTFType to GLTFAccessorType, fix verbose prints, document GLTFAccessor ([GH-90560](https://github.com/godotengine/godot/pull/90560)).
- Fix error when loading SVG imported as Image ([GH-90573](https://github.com/godotengine/godot/pull/90573)).
- Fix FBX texture path resolving ([GH-90635](https://github.com/godotengine/godot/pull/90635)).
- Remove unused Make Streamable option from scene import dialog ([GH-90722](https://github.com/godotengine/godot/pull/90722)).
- Fix FBX and glTF when root nodes are skeleton bones ([GH-90789](https://github.com/godotengine/godot/pull/90789)).
- Resolve bind poses from FBX clusters instead of FBX poses ([GH-91036](https://github.com/godotengine/godot/pull/91036)).
- fbx: Avoid name conflict with humanoid "Root" bone ([GH-91045](https://github.com/godotengine/godot/pull/91045)).
- Fix errors/crashes related to skipped imports ([GH-91078](https://github.com/godotengine/godot/pull/91078)).
- Enable tangents in blend shape format when using normals ([GH-91372](https://github.com/godotengine/godot/pull/91372)).
- FBX: Fix handling missing skins using ufbx importer ([GH-91526](https://github.com/godotengine/godot/pull/91526)).
- FBX: Print ufbx load warnings on import ([GH-91529](https://github.com/godotengine/godot/pull/91529)).
- GLTF export improvements ([GH-91783](https://github.com/godotengine/godot/pull/91783)).
- Editor: Ensure font image rows/columns are positive ([GH-91829](https://github.com/godotengine/godot/pull/91829)).
- Improve memory usage for image import and `PortableCompressedTexture2D` ([GH-92179](https://github.com/godotengine/godot/pull/92179)).
- FBX: Change trimming default and use FBX2glTF for compatibility ([GH-92197](https://github.com/godotengine/godot/pull/92197)).
- Fix script properties being lost and prevent node reference corruption upon scene reimport ([GH-92279](https://github.com/godotengine/godot/pull/92279)).
- Fix reimporting assets with csv in the project ([GH-92320](https://github.com/godotengine/godot/pull/92320)).
- Fix GLTFDocument so it can export CSG Meshes correctly ([GH-92368](https://github.com/godotengine/godot/pull/92368)).
- Avoid crash when importing .glsl in headless ([GH-92539](https://github.com/godotengine/godot/pull/92539)).
- Rename FBX2glTF binary path setting back to 4.2 name ([GH-92571](https://github.com/godotengine/godot/pull/92571)).
- Fix same importer will be added multiple times in `get_importers_for_extension` ([GH-92718](https://github.com/godotengine/godot/pull/92718)).
- Fix Keep/Skip File import selection crash ([GH-92815](https://github.com/godotengine/godot/pull/92815)).
- Fix incorrect camera transform of animation view in the import window ([GH-92974](https://github.com/godotengine/godot/pull/92974)).
- [Image Font Importer] Fix reading advance after hex/dec range ([GH-93074](https://github.com/godotengine/godot/pull/93074)).
- Unset the owner of `ImporterMeshInstance3D` before adding it to skeleton's child ([GH-93117](https://github.com/godotengine/godot/pull/93117)).
- [Image Font Importer] Adds support for `\uXXXX` in the kerning config strings ([GH-93119](https://github.com/godotengine/godot/pull/93119)).
- Fix ProgressDialog crash when importing TTF font ([GH-93161](https://github.com/godotengine/godot/pull/93161)).
- Avoid crashing when scene import settings are empty ([GH-93284](https://github.com/godotengine/godot/pull/93284)).
- Remove `monospace/fixed-width` from the `ResourceImporterImageFont` name and description ([GH-93337](https://github.com/godotengine/godot/pull/93337)).
- Fix `browse_dialog` in Blender scene importer to accept files ([GH-93411](https://github.com/godotengine/godot/pull/93411)).
- Make Basis Universal import quiet unless engine is in verbose mode ([GH-93442](https://github.com/godotengine/godot/pull/93442)).
- Fix default collision shape on imported rigidbody ([GH-93506](https://github.com/godotengine/godot/pull/93506)).
- Editor: Fix importers add-ons after 93238 ([GH-93518](https://github.com/godotengine/godot/pull/93518)).
- Use Hermite instead of Bezier for glTF spline interpolation ([GH-93597](https://github.com/godotengine/godot/pull/93597)).
- Fix reimport by scan parsing dependency paths incorrectly ([GH-93765](https://github.com/godotengine/godot/pull/93765)).
- Fix adding a translation CSV results in errors on initial import for many types of resources ([GH-93919](https://github.com/godotengine/godot/pull/93919)).
- Move GL