name: godot-modernize-gdscript version: 1.0.0 displayName: "Godot GDScript 2.0 Modernizer" description: > Modernizes GDScript 1.0 (Godot 3.x) code to GDScript 2.0 (Godot 4.x) syntax. Converts yield to await, onready to @onready, export to @export, setget to property syntax, and adds optional static typing. Essential for Godot 3.x to 4.x migration projects. author: "Asreonn" license: MIT category: game-development type: tool difficulty: intermediate audience: [developers, migrators] keywords:
- godot
- gdscript
- modernization
- migration
- godot3
- godot4
- yield
- await
- static-typing
platforms: [macos, linux, windows]
repository: https://github.com/asreonn/godot-superpowers
homepage: https://github.com/asreonn/godot-superpowers#readme
filesystem:
read:
- "${PROJECT_ROOT}/**/*.gd"
- "${PROJECT_ROOT}/project.godot" write:
- "${PROJECT_ROOT}/**/*.gd" deny:
- "**/.env*"
- "**/secrets*"
- "**/*.key" behavior: timeout: 300 retry: 2 cache: true interactive: true
Godot GDScript 2.0 Modernizer
Converts GDScript 1.0 (Godot 3.x) patterns to GDScript 2.0 (Godot 4.x) syntax.
Core Conversions
This skill performs five key modernizations:
| GDScript 1.0 | GDScript 2.0 | Pattern |
|---|---|---|
yield(object, "signal") | await object.signal | Async operations |
onready var | @onready var | Node references |
export var | @export var | Inspector variables |
var x setget set_x, get_x | Property syntax | Getters/setters |
var health = 100 | var health: int = 100 | Static typing |
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute:
1. Verify Godot Project (5 seconds)
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- Inform user this skill only works on Godot projects
- STOP here
If IS a Godot project:
- Proceed to step 2
2. Detect GDScript Version (10 seconds)
# Check for Godot 3.x patterns
echo "=== Detecting GDScript 1.0 Patterns ==="
echo "yield statements:"
grep -rn "yield(" --include="*.gd" . | wc -l
echo "onready declarations:"
grep -rn "^onready var" --include="*.gd" . | wc -l
echo "export declarations:"
grep -rn "^export var\|^export(int)\|^export(float)" --include="*.gd" . | wc -l
echo "setget properties:"
grep -rn "setget" --include="*.gd" . | wc -l
echo "untyped variables:"
grep -rn "^var [a-z]" --include="*.gd" . | grep -v ":" | wc -l
3. Present Findings
Show the user:
=== GDScript 2.0 Modernization Analysis ===
Project: [project name]
Current: GDScript 1.0 (Godot 3.x style)
Patterns to modernize:
- yield statements: X
- onready variables: X
- export variables: X
- setget properties: X
- untyped variables: X
Total: X modernizations needed
Modernization includes:
✓ yield → await conversion
✓ onready → @onready
✓ export → @export
✓ setget → property syntax
✓ Optional static typing
✓ Git commit per file
✓ Backup before changes
Would you like me to:
1. Modernize all files (recommended)
2. Show detailed breakdown first
3. Select specific conversions
4. Cancel
4. Wait for User Choice
- If 1 (Proceed): Start Phase 2 immediately
- If 2 (Details): Show file-by-file breakdown, then offer to proceed
- If 3 (Selective): Ask which conversions to apply
- If 4 (Cancel): Exit skill
Phase 1: Analysis & Inventory
1.1 Create File Inventory
# Find all .gd files
find . -name "*.gd" -type f | sort > /tmp/gdscript_files.txt
wc -l /tmp/gdscript_files.txt
echo "files found"
1.2 Analyze Each File
For each .gd file, detect patterns:
# Analyze patterns per file
for file in $(cat /tmp/gdscript_files.txt); do
echo "=== $file ==="
grep -c "yield(" "$file" 2>/dev/null || echo 0
grep -c "^onready var" "$file" 2>/dev/null || echo 0
grep -c "^export" "$file" 2>/dev/null || echo 0
grep -c "setget" "$file" 2>/dev/null || echo 0
done
1.3 Create Modernization Plan
Modernization Plan:
===================
1. yield → await conversions (X files)
2. onready → @onready (X files)
3. export → @export (X files)
4. setget → properties (X files)
5. Static typing (X files)
Total files to modify: X
Estimated time: Auto (user doesn't wait)
Backup created: YES (git tag)
Rollback available: YES
Phase 2: Modernization Operations
Conversion A: yield → await
Detection:
grep -rn "yield(" --include="*.gd" .
Common Patterns:
| Old Pattern | New Pattern |
|---|---|
yield(get_tree().create_timer(1.0), "timeout") | await get_tree().create_timer(1.0).timeout |
yield(timer, "timeout") | await timer.timeout |
yield(animation_player, "animation_finished") | await animation_player.animation_finished |
yield(get_tree(), "idle_frame") | await get_tree().process_frame |
Transformation Process:
-
Extract yield statement
# Before yield(get_tree().create_timer(2.0), "timeout") -
Convert to await
# After await get_tree().create_timer(2.0).timeout -
Handle variable assignment
# Before var result = yield(async_function(), "completed") # After var result = await async_function()
Implementation:
# Pseudo-code for replacement
def convert_yield_to_await(line):
# Pattern: yield(object, "signal_name")
# Convert to: await object.signal_name
import re
pattern = r'yield\(([^,]+),\s*"([^"]+)"\)'
replacement = r'await \1.\2'
return re.sub(pattern, replacement, line)
Conversion B: onready → @onready
Detection:
grep -rn "^onready var" --include="*.gd" .
Transformation:
# Before
onready var player = $Player
onready var health_bar = $UI/HealthBar
# After
@onready var player: Node = $Player
@onready var health_bar: ProgressBar = $UI/HealthBar
Process:
- Replace
onreadywith@onready - Add type hints where detectable
- Keep variable name and initialization
Type Inference (Optional):
# If $Player is CharacterBody2D in scene
@onready var player: CharacterBody2D = $Player
# If $Timer is Timer node
@onready var _timer: Timer = $Timer
Conversion C: export → @export
Detection:
grep -rn "^export" --include="*.gd" .
Transformation:
# Before
export var speed = 200
export(int) var max_health = 100
export(float) var jump_force = 500.0
export(String) var character_name = "Player"
export(NodePath) var target_path
# After
@export var speed: float = 200.0
@export var max_health: int = 100
@export var jump_force: float = 500.0
@export var character_name: String = "Player"
@export var target_path: NodePath
Export Types Mapping:
| Old Export | New Export | Type |
|---|---|---|
export(int) | @export var x: int | Integer |
export(float) | @export var x: float | Float |
export(String) | @export var x: String | String |
export(bool) | @export var x: bool | Boolean |
export(Color) | @export var x: Color | Color |
export(Vector2) | @export var x: Vector2 | Vector2 |
export(Vector3) | @export var x: Vector3 | Vector3 |
export(NodePath) | @export var x: NodePath | NodePath |
export var | @export var x: Type | Inferred |
Conversion D: setget → Property Syntax
Detection:
grep -rn "setget" --include="*.gd" .
Transformation:
# Before (GDScript 1.0)
var health = 100 setget set_health, get_health
func set_health(value):
health = clamp(value, 0, max_health)
health_changed.emit(health)
func get_health():
return health
# After (GDScript 2.0)
var health: int = 100:
set(value):
health = clamp(value, 0, max_health)
health_changed.emit(health)
get:
return health
Process:
- Remove
setgetfrom variable declaration - Add colon after type
- Inline setter and getter
- Use
set(value):andget:syntax
Complex Example:
# Before
var score = 0 setget set_score
func set_score(value):
score = max(0, value)
update_ui()
# After
var score: int = 0:
set(value):
score = max(0, value)
update_ui()
Conversion E: Static Typing (Optional)
Detection:
grep -rn "^var [a-z_]* = " --include="*.gd" . | grep -v ":"
Type Inference:
| Value | Inferred Type |
|---|---|
= 100 | int |
= 3.14 | float |
= "text" | String |
= true | bool |
= Vector2(x, y) | Vector2 |
= $Node | Node type from scene |
Transformation:
# Before
var health = 100
var speed = 200.5
var name = "Player"
var active = true
var position = Vector2.ZERO
# After
var health: int = 100
var speed: float = 200.5
var name: String = "Player"
var active: bool = true
var position: Vector2 = Vector2.ZERO
Phase 3: Execution & Safety
3.1 Create Git Baseline
# Create backup tag
git tag gdscript1-baseline-$(date +%Y%m%d-%H%M%S)
# Stage any current changes
git add .
git commit -m "Baseline: Pre-GDScript 2.0 modernization" || echo "No changes to commit"
3.2 Process Files Sequentially
For each .gd file:
# Process file
python3 << 'EOF'
import re
def modernize_gdscript(content):
# Conversion 1: yield → await
content = re.sub(
r'yield\(([^,]+),\s*"([^"]+)"\)',
r'await \1.\2',
content
)
# Conversion 2: onready → @onready
content = re.sub(
r'^onready var',
'@onready var',
content,
flags=re.MULTILINE
)
# Conversion 3: export → @export (simple)
content = re.sub(
r'^export var',
'@export var',
content,
flags=re.MULTILINE
)
return content
# Read, process, write
with open("script.gd", "r") as f:
content = f.read()
new_content = modernize_gdscript(content)
with open("script.gd", "w") as f:
f.write(new_content)
EOF
3.3 Commit Per File
# After each file modification
git add "$file"
git commit -m "Modernize: $file to GDScript 2.0
- Converted yield to await
- Updated onready to @onready
- Migrated export to @export
- Applied static typing"
3.4 Validation After Each File
# Validate syntax
godot --headless --quit-after 2 project.godot 2>&1 | grep -i "error" && {
echo "Syntax error detected in $file"
git reset --hard HEAD~1
echo "Reverted changes to $file"
}
Phase 4: Verification & Testing
4.1 Syntax Validation
# Check all modified files for syntax errors
for file in $(git diff --name-only HEAD~10..HEAD | grep "\.gd$"); do
echo "Checking $file..."
# Basic syntax check
gdscript_tool --check "$file" 2>/dev/null || echo "Manual review needed: $file"
done
4.2 Pattern Verification
# Verify no old patterns remain
echo "Checking for remaining GDScript 1.0 patterns..."
echo "yield statements remaining:"
grep -rn "yield(" --include="*.gd" . | wc -l
echo "onready remaining:"
grep -rn "^onready var" --include="*.gd" . | wc -l
echo "export remaining:"
grep -rn "^export var\|^export(" --include="*.gd" . | wc -l
4.3 Godot Project Test
# Open project in Godot to verify
godot --editor project.godot &
sleep 5
# Check for errors
# (User should visually verify no red errors in Output panel)
Examples
Example 1: Complete Script Modernization
Before (GDScript 1.0):
extends CharacterBody2D
export var speed = 200
export(int) var health = 100
onready var sprite = $Sprite
onready var animation = $AnimationPlayer
var damage = 10 setget set_damage
func set_damage(value):
damage = clamp(value, 0, 100)
func _ready():
yield(get_tree().create_timer(1.0), "timeout")
start_game()
func take_damage(amount):
health -= amount
if health <= 0:
yield(play_death_animation(), "completed")
queue_free()
func play_death_animation():
animation.play("death")
yield(animation, "animation_finished")
After (GDScript 2.0):
extends CharacterBody2D
@export var speed: float = 200.0
@export var health: int = 100
@onready var sprite: Sprite2D = $Sprite
@onready var animation: AnimationPlayer = $AnimationPlayer
var damage: int = 10:
set(value):
damage = clamp(value, 0, 100)
func _ready():
await get_tree().create_timer(1.0).timeout
start_game()
func take_damage(amount: int) -> void:
health -= amount
if health <= 0:
await play_death_animation()
queue_free()
func play_death_animation() -> void:
animation.play("death")
await animation.animation_finished
Example 2: Signal Connection Modernization
Before:
func _ready():
button.connect("pressed", self, "_on_button_pressed")
timer.connect("timeout", self, "_on_timeout")
func _on_button_pressed():
pass
func _on_timeout():
pass
After:
func _ready():
button.pressed.connect(_on_button_pressed)
timer.timeout.connect(_on_timeout)
func _on_button_pressed() -> void:
pass
func _on_timeout() -> void:
pass
Success Criteria
Modernization complete when:
- ✓ Zero
yield(statements remain (converted to await) - ✓ Zero
onready varremain (converted to @onready) - ✓ Zero
export varremain (converted to @export) - ✓ Zero
setgetremain (converted to property syntax) - ✓ All scripts compile without errors
- ✓ No functional changes (behavior identical)
- ✓ Git history shows clear modernization commits
Common Issues & Solutions
Issue 1: yield with function calls
Problem:
var result = yield(load_data(), "completed")
Solution:
var result = await load_data()
Issue 2: Coroutines with state
Problem:
func process():
yield(do_step_1(), "completed")
yield(do_step_2(), "completed")
Solution:
func process() -> void:
await do_step_1()
await do_step_2()
Issue 3: setget with only getter or setter
Problem:
var score = 0 setget , get_score # Only getter
Solution:
var score: int = 0:
get:
return score
Rollback Procedure
If modernization causes issues:
# Find baseline tag
git tag | grep "gdscript1-baseline"
# Reset to pre-modernization state
git reset --hard <baseline-tag>
# Or revert specific commits
git revert <modernization-commit-hash>
Integration with Other Skills
Use before:
godot-refactor- Modernize syntax first, then refactor architecturegodot-migrate-tilemap- Update code before TileMap migration
Use after:
- Project conversion from Godot 3.x to 4.x
godot-organize-project- Organize files first
This skill automates the tedious parts of GDScript 1.0 → 2.0 migration while preserving exact functionality.