Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
6607b5b
Convert project to runnable state in godot 4.6
Razoric480 Mar 20, 2026
06b55ea
Remove .godot local cache folder from git tracking
NathanLovato Mar 21, 2026
86ef3dd
Update signal calls to use first class signals and function references
NathanLovato Mar 21, 2026
bc98178
Address errors and warnings in Graph.gd
NathanLovato Mar 21, 2026
60df445
Fix errors in Revealer.gd now that some methods were renamed
NathanLovato Mar 21, 2026
8f39eea
Edit tween methods and callback to use direct function references
NathanLovato Mar 21, 2026
e79ebf7
Fix various errors and partially fix slice editor
Razoric480 Mar 21, 2026
d5d545b
Assign original font sizes to TSCN theme overrides
Razoric480 Mar 22, 2026
5eccd84
Fix code editor
Razoric480 Mar 22, 2026
57b6334
Update export presets
Razoric480 Mar 23, 2026
bd13aab
Update build paths
Razoric480 Mar 23, 2026
3090809
Update executables and test export
Razoric480 Mar 23, 2026
3abc086
Fix lessons through to 15
Razoric480 Mar 24, 2026
ac6213e
Fix remaining lessons and practices
Razoric480 Mar 24, 2026
926c62c
Update highlighter
Razoric480 Mar 25, 2026
f1b6d8f
Fix welcome screen main menu buttons popping into place
Razoric480 Mar 25, 2026
265e513
Set default font size
Razoric480 Mar 25, 2026
2bb9c94
Remove errant print statement
Razoric480 Mar 25, 2026
3c31ddb
Fix null unicode error spam in slice editor
Razoric480 Mar 25, 2026
49a691b
Change JavaScript feature to web. Add missing scene
Razoric480 Mar 25, 2026
93ab525
Fix scroll container; fix hints not sized to text
Razoric480 Mar 25, 2026
6da5c5d
Fix revealer spinning
Razoric480 Mar 25, 2026
fb3ec6c
Rewrite coroutine codes
Razoric480 Mar 25, 2026
c7011b8
Fix end screen
Razoric480 Mar 25, 2026
256c637
Fix debugger highlight and use code font for code examples
Razoric480 Mar 25, 2026
5d5dce5
Fix lesson done particles
Razoric480 Mar 25, 2026
689d84a
Fix robot particles and report form
Razoric480 Mar 25, 2026
2702178
Fix executable name
Razoric480 Mar 25, 2026
c46aed1
Enable ETC2 ASTC for MacOS
Razoric480 Mar 25, 2026
ea84822
Fix error message visual hidden
Razoric480 Mar 25, 2026
4e81b1f
Add temporary tag
Razoric480 Mar 25, 2026
687583e
Hide percentages when progress bar is used as popup top bar
Razoric480 Mar 25, 2026
b3c4413
Hide percentage in settings screen topbar
Razoric480 Mar 25, 2026
7968de6
Fix wrong UUID for syntax highlighter
Razoric480 Mar 25, 2026
5c5dfe3
Fix credits label
Razoric480 Mar 25, 2026
707fdef
Fix quiz answers not being word wrapped changing quiz sizes
Razoric480 Mar 25, 2026
f06ea99
Fix breadcrumbs changing size
Razoric480 Mar 25, 2026
13c39c1
Fix outliner and main menu buttons
Razoric480 Mar 25, 2026
54ec0bb
Throttle scrolling delta by event factor
Razoric480 Mar 25, 2026
d96e02c
Apply some TextEdit overrides to CodeEdit in theme
Razoric480 Mar 26, 2026
b89bc4a
Replace all font size overrides with theme variations for theme manager
Razoric480 Mar 26, 2026
00c6453
Add default font size handling for dyslexia font
Razoric480 Mar 26, 2026
83fca26
Revert "Throttle scrolling delta by event factor"
Razoric480 Mar 26, 2026
0320f4d
Fix unconverted script
Razoric480 Mar 26, 2026
eaaa15f
Fudge sleep mode usage pending engine fix
Razoric480 Mar 26, 2026
e3da781
Stabilize scrolling with factor from input event
Razoric480 Mar 26, 2026
5dc2134
Remove frame limit on web due to engine bug
Razoric480 Mar 26, 2026
7716ed1
Fix typo; small web
Razoric480 Mar 26, 2026
d836696
Fix MacOS not exporting LGD and BBCode files
Razoric480 Mar 27, 2026
fee1fe5
Center pages for wide aspect ratios
Razoric480 Mar 27, 2026
a9685ee
Remove default full screen from web build
Razoric480 Mar 27, 2026
8477313
Wrap course lesson detail title
Razoric480 Mar 28, 2026
55c6c94
Fix font defaults editor hint inversion; hide framerate cap on web
Razoric480 Mar 28, 2026
83500e2
Fix editor font changes not applying
Razoric480 Mar 28, 2026
8c390fc
Prevent profile from saving multiple times
Razoric480 Mar 28, 2026
373e979
Immunize GDScript credits label from font changes
Razoric480 Mar 28, 2026
9c0aa2a
Upgrade all resources to Godot 4.6, remove direct theme references on…
NathanLovato Mar 29, 2026
be86d5a
Fix error when trying to change font size slider when using dyslexia …
NathanLovato Mar 29, 2026
fef4c34
Avoid unnecessary processing when changing settings that do not requi…
NathanLovato Mar 29, 2026
cd65044
Run GDScript-formatter on non-course scripts
Razoric480 Mar 29, 2026
26d8a48
Fix course tester and make sure all tests pass
Razoric480 Mar 29, 2026
75fb604
Fix a couple runtime errors
Razoric480 Mar 29, 2026
eff0108
Fix formatter snafu
Razoric480 Mar 29, 2026
9eb1130
Re-add scene root test support
Razoric480 Mar 29, 2026
b8e7c1d
Rename const for consistency
Razoric480 Mar 29, 2026
74cc40c
Wrap error messages; account for GDScriptAnalyzer by forcing class_name
Razoric480 Apr 1, 2026
3c6730a
Reduce warnings in script verifier
Razoric480 Apr 1, 2026
7659440
Add TODO regarding settings screen
Razoric480 Apr 1, 2026
3a40f6c
Rebase editor to 4.6.2
Razoric480 Apr 1, 2026
81fe601
Clean up GDScriptAnalyzer workaround
Razoric480 Apr 2, 2026
3f7f3a8
Remove scroll to reading
Razoric480 Apr 3, 2026
65e4916
Boil down lesson read to practice buttons becoming visible; remove bl…
Razoric480 Apr 3, 2026
978f1b1
Clean up content block scene
Razoric480 Apr 3, 2026
2710ea1
Cleanup some onreadies
Razoric480 Apr 3, 2026
872c104
Deal with more onreadies
Razoric480 Apr 3, 2026
b590372
Fix practice nodes forgetting @exported variables
Razoric480 Apr 3, 2026
3cbb300
Remove duplicate code
Razoric480 Apr 3, 2026
a14bc40
Reduce warning count by ~100
Razoric480 Apr 5, 2026
5a301d3
Remove all startup warnings
Razoric480 Apr 5, 2026
ed0d4ec
Remove codeblock tag definition
Razoric480 Apr 5, 2026
96d5b18
Remove more runtime warnings
Razoric480 Apr 5, 2026
f92e3da
Fix some non-course warnings
Razoric480 Apr 9, 2026
8f79767
Rework the URL slug system
Razoric480 Apr 9, 2026
dc89832
Wrestle slug system to work with modern and legacy; rename all practi…
Razoric480 Apr 10, 2026
339f030
Remove test tag
Razoric480 Apr 10, 2026
cb72463
Fix rebase breaks
Razoric480 Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GODOT_VERSION=3.6.2
GODOT_VERSION=4.6.2
TEMPLATES_REPO=Razoric480/custom-godot-builder
BUTLER_API_KEY=
ITCHIO_USERNAME=
ITCHIO_GAME=
ITCHIO_GAME=
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ html_export/index.html


# Godot-specific ignores
.godot/
.import/
export.cfg

Expand Down Expand Up @@ -36,4 +37,4 @@ __pycache__/
7z.so
butler
godot_server.x86_64
libc7zip.so
libc7zip.so
39 changes: 34 additions & 5 deletions BigFont.tres
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[gd_resource type="FontFile" format=3 uid="uid://42m3ip606cyk"]

[sub_resource type="DynamicFontData" id=9]
font_path = "res://addons/gut/fonts/LobsterTwo-BoldItalic.ttf"
[sub_resource type="FontFile" id="9"]
subpixel_positioning = 0
msdf_pixel_range = 14
msdf_size = 128
cache/0/variation_coordinates = {}
cache/0/face_index = 0
cache/0/embolden = 0.0
cache/0/transform = Transform2D(1, 0, 0, 1, 0, 0)
cache/0/spacing_top = 0
cache/0/spacing_bottom = 0
cache/0/spacing_space = 0
cache/0/spacing_glyph = 0
cache/0/baseline_offset = 0.0
cache/0/16/0/ascent = 0.0
cache/0/16/0/descent = 0.0
cache/0/16/0/underline_position = 0.0
cache/0/16/0/underline_thickness = 0.0
cache/0/16/0/scale = 1.0

[resource]
size = 40
font_data = SubResource( 9 )
fallbacks = Array[Font]([SubResource("9")])
cache/0/variation_coordinates = {}
cache/0/face_index = 0
cache/0/embolden = 0.0
cache/0/transform = Transform2D(1, 0, 0, 1, 0, 0)
cache/0/spacing_top = 0
cache/0/spacing_bottom = 0
cache/0/spacing_space = 0
cache/0/spacing_glyph = 0
cache/0/baseline_offset = 0.0
cache/0/16/0/ascent = 0.0
cache/0/16/0/descent = 0.0
cache/0/16/0/underline_position = 0.0
cache/0/16/0/underline_thickness = 0.0
cache/0/16/0/scale = 1.0
6 changes: 3 additions & 3 deletions BigFontTheme.tres
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[gd_resource type="Theme" load_steps=2 format=2]
[gd_resource type="Theme" format=3 uid="uid://cb5orhoawseqj"]

[ext_resource path="res://BigFont.tres" type="DynamicFont" id=1]
[ext_resource type="FontFile" uid="uid://42m3ip606cyk" path="res://BigFont.tres" id="1"]

[resource]
default_font = ExtResource( 1 )
default_font = ExtResource("1")
50 changes: 50 additions & 0 deletions CourseIndexPaths.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class_name CourseIndexPaths
extends RefCounted

const DEFAULT_COURSE_INDEX := "learn-gdscript"

const COURSES := {
"learn-gdscript": "res://course/CourseLearnGDScriptIndex.gd"
}

const COURSE_SLUG_ALIASES := {
"course": "learn-gdscript"
}

static var _course_index_cache := {}


static func build_all_practice_slugs() -> void:
for course_index: String in COURSES:
var index := get_course_index_instance(course_index)
for i in index.get_lessons_count():
var lesson := NavigationManager.get_navigation_resource(index.get_lesson_path(i))
var lesson_slug: String = index.get_lesson_slug(i)
for l in BBCodeUtils.get_lesson_practice_count(lesson):
var practice := BBCodeUtils.get_lesson_practice(lesson, l)
var id := BBCodeUtils.get_practice_id(practice)
index.set_practice_slug(lesson_slug, id, practice)


static func get_course_index_instance(course_id: String = DEFAULT_COURSE_INDEX) -> CourseIndex:
var effective_id := course_id
if not COURSES.has(course_id) and COURSE_SLUG_ALIASES.has(course_id):
effective_id = COURSE_SLUG_ALIASES[course_id]

var index: CourseIndex = _course_index_cache.get(effective_id, null)
if index:
return index

var index_path: String = COURSES.get(effective_id, "")
if not index_path:
push_error("Invalid course %s and no alias found" % [course_id])
return null

var index_script: GDScript = load(index_path)
if not index_script:
push_error("Course %s at %s failed to load" % [course_id, index_path])
return null

index = index_script.new()
_course_index_cache[course_id] = index
return index
1 change: 1 addition & 0 deletions CourseIndexPaths.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://b3461ed37w8i
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ For code contributions, be sure to bookmark our GDScript code style guide: [GDQu

You can also run the project straight in Godot by cloning the repository and importing the folder into the engine.

We only recommend importing the app in Godot to study its source code or [contribute](#how-to-contribute). You will need **Godot 3.5 LTS** or a more recent stable version of Godot 3. Otherwise, it won't run.
We only recommend importing the app in Godot to study its source code or [contribute](#how-to-contribute). You will need **Godot 4.6.1 LTS** or a more recent stable version of Godot 4. Otherwise, it won't run.

Please note that practice errors will trigger the debugger and pause execution in Godot, unlike when using the release build. That's normal, and you'll need to continue execution by pressing <kbd>F7</kbd> when that happens.

Expand Down
14 changes: 14 additions & 0 deletions RecolorHighlighter.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[gd_scene format=3 uid="uid://kl5p8gbewug5"]

[sub_resource type="GDScript" id="GDScript_1xs7u"]
script/source = "extends Node


func _ready() -> void:
var highlighter: CodeHighlighter = load(CodeEditorEnhancer.GDSCRIPT_SYNTAX_HIGHLIGHTER_PATH)
CodeEditorEnhancer.enhance_highlighter(highlighter)
ResourceSaver.save(highlighter, CodeEditorEnhancer.GDSCRIPT_SYNTAX_HIGHLIGHTER_PATH)
"

[node name="RecolorHighlighter" type="Node" unique_id=1605165953]
script = SubResource("GDScript_1xs7u")
16 changes: 9 additions & 7 deletions addons/ColorPickerPresets/ColorPickerPresets.gd
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
tool
@tool
extends EditorPlugin

const PRESETS_FILENAME := 'presets.hex'


func _enter_tree() -> void:
var presets := PoolColorArray()
var presets_raw := PoolStringArray()
var presets_file := File.new()
var presets_path: String = get_script().resource_path.get_base_dir().plus_file(PRESETS_FILENAME)
var presets := PackedColorArray()
var presets_raw := PackedStringArray()
var presets_path: String = get_script().resource_path.get_base_dir().path_join(PRESETS_FILENAME)
var presets_file := FileAccess.open(presets_path, FileAccess.READ)

if presets_file.open(presets_path, File.READ) == OK:
if presets_file:
presets_raw = presets_file.get_as_text().split("\n")
presets_file.close()

Expand All @@ -19,5 +19,7 @@ func _enter_tree() -> void:
presets.push_back(Color(hex))

get_editor_interface().get_editor_settings().set_project_metadata(
"color_picker", "presets", presets
"color_picker",
"presets",
presets,
)
1 change: 1 addition & 0 deletions addons/ColorPickerPresets/ColorPickerPresets.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://igi6vovxnanf
37 changes: 21 additions & 16 deletions app-practice-screen.png.import
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/app-practice-screen.png-2803de6b8f7978f679c93f9f4836ed49.stex"
type="CompressedTexture2D"
uid="uid://bt0j7w7yvxtaq"
path="res://.godot/imported/app-practice-screen.png-2803de6b8f7978f679c93f9f4836ed49.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://app-practice-screen.png"
dest_files=[ "res://.import/app-practice-screen.png-2803de6b8f7978f679c93f9f4836ed49.stex" ]
dest_files=["res://.godot/imported/app-practice-screen.png-2803de6b8f7978f679c93f9f4836ed49.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
37 changes: 21 additions & 16 deletions asset_lib_icon.png.import
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/asset_lib_icon.png-195a8230d8f4e21994f87ec4f754d7d9.stex"
type="CompressedTexture2D"
uid="uid://cpc4ehjjs6w2w"
path="res://.godot/imported/asset_lib_icon.png-195a8230d8f4e21994f87ec4f754d7d9.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://asset_lib_icon.png"
dest_files=[ "res://.import/asset_lib_icon.png-195a8230d8f4e21994f87ec4f754d7d9.stex" ]
dest_files=["res://.godot/imported/asset_lib_icon.png-195a8230d8f4e21994f87ec4f754d7d9.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
2 changes: 1 addition & 1 deletion autoload/Events.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
extends Node

signal lesson_started(lesson)
signal lesson_read(lesson)
signal lesson_completed(lesson)
signal lesson_reading_block(block, previous_blocks)
signal quiz_completed(quiz)
signal practice_started(practice)
signal practice_run_completed()
Expand Down
1 change: 1 addition & 0 deletions autoload/Events.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://bko768uqjf5ge
32 changes: 15 additions & 17 deletions autoload/GDScriptErrorDatabase.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ const CSV_IDENTIFIER_FIELD := "error_code"
const CSV_EXPLANATION_FIELD := "error_explanation"
const CSV_SUGGESTION_FIELD := "error_suggestion"

var _main_table := {}
var _translated_table := {}
var _main_table := { }


func _init():
Expand All @@ -24,7 +23,7 @@ func get_message(error_code: int) -> Dictionary:
if error_code == -1:
return message

if not _main_table.empty() and _main_table.has(error_code):
if not _main_table.is_empty() and _main_table.has(error_code):
var record = _main_table[error_code] as DatabaseRecord
if record:
message.explanation = record.explanation
Expand All @@ -34,27 +33,26 @@ func get_message(error_code: int) -> Dictionary:


func _load_csv_file(file_path: String) -> Dictionary:
var database_file = File.new()
if file_path.empty() or not database_file.file_exists(file_path):
if file_path.is_empty() or not FileAccess.file_exists(file_path):
printerr(
"Failed to open the error database source at '%s': File does not exist." % [file_path]
"Failed to open the error database source at '%s': File does not exist." % [file_path],
)
return {}
return { }

var error = database_file.open(file_path, File.READ)
if error != OK:
var database_file := FileAccess.open(file_path, FileAccess.READ)
if not database_file:
printerr(
"Failed to open the error database source at '%s': Error code %d" % [file_path, error]
"Failed to open the error database source at '%s': Error code %d" % [file_path, FileAccess.get_open_error()],
)
return {}
return { }

var table = _parse_csv_file(database_file)
var table := _parse_csv_file(database_file)
database_file.close()
return table


func _parse_csv_file(file: File) -> Dictionary:
var parsed := {}
func _parse_csv_file(file: FileAccess) -> Dictionary:
var parsed := { }
if not file.is_open():
return parsed

Expand All @@ -71,7 +69,7 @@ func _parse_csv_file(file: File) -> Dictionary:
return parsed

# Loop while there is content in the file left.
while file.get_position() < file.get_len():
while file.get_position() < file.get_length():
var line = file.get_csv_line(CSV_DELIMITER)
# Empty or invalid line, ignore it.
if line.size() == 0 or line.size() != header.size():
Expand All @@ -85,11 +83,11 @@ func _parse_csv_file(file: File) -> Dictionary:
error_value = GDScriptCodes.ErrorCode[error_code]
elif GDQuestCodes.ErrorCode.has(error_code):
error_value = GDQuestCodes.ErrorCode[error_code]

# It seems that the name of the error in the CSV file is invalid, reporting and skipping.
if error_value == -1:
printerr(
"Bad error database record '%s': No such error or warning code." % [error_code]
"Bad error database record '%s': No such error or warning code." % [error_code],
)
continue

Expand Down
1 change: 1 addition & 0 deletions autoload/GDScriptErrorDatabase.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://33sq2f0q33x4
Loading