Added rider plugin and turned Empowered Action into a forge-resources-managed ability

This commit is contained in:
2026-04-03 15:33:46 +02:00
parent c1108e96d7
commit 673368a200
27 changed files with 854 additions and 9 deletions

View File

@@ -0,0 +1,23 @@
@tool
## Simple JSON helpers for editor/runtime use.
## Keeps file and JSON parsing concerns out of feature code.
class_name JsonUtils
static func load_from_file(path: String) -> Variant:
# Returns parsed JSON value (Dictionary/Array/etc.) or null on error.
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
push_warning("JsonUtils: Failed to open file: %s" % path)
return null
var text := file.get_as_text()
file.close()
var data: Variant = JSON.parse_string(text)
if data == null:
push_warning("JsonUtils: Invalid JSON in file: %s" % path)
return null
return data
static func load_dict_from_file(path: String) -> Dictionary:
# Returns Dictionary or empty {} on error.
var data := load_from_file(path) as Dictionary
return data

View File

@@ -0,0 +1 @@
uid://bci4kmk7h4j6a

View File

@@ -0,0 +1,112 @@
@tool
extends RefCounted
class_name RiderLocatorService
var _installations_found: Array = []
var _thread: Thread = null
func get_installations() -> Array:
var result: Array = RiderLocator.new().get_installations() # from the gdextension
return result
# todo
func fix_external_editor_if_supplied_in_commandline(_settings_service: EditorSettingsService, editor_settings: EditorSettings) -> bool:
# When Godot is started from Rider (or vice versa), we may receive the Rider path
# via command-line so we can keep Godot's external editor setting in sync.
# Supported form (only this one):
# --my_rider_path="/absolute/path/to/rider with possible spaces"
var args : Array = OS.get_cmdline_args()
var provided_rider_path := ""
for a_raw in args:
var a: String = str(a_raw)
if a.begins_with("--my_rider_path="):
provided_rider_path = a.substr("--my_rider_path=".length())
break
if provided_rider_path.is_empty():
return false
provided_rider_path = trim_quotes(provided_rider_path)
# Validate existence (file or dir)
var looks_existing := FileAccess.file_exists(provided_rider_path) || DirAccess.dir_exists_absolute(provided_rider_path)
if looks_existing:
_settings_service.set_external_editor_path(editor_settings, provided_rider_path)
print("Rider path provided via CLI (my_rider_path): ", provided_rider_path)
return true
else:
push_warning("my_rider_path was provided but does not exist: %s" % [provided_rider_path])
return false
func trim_quotes(s: String) -> String:
if s.begins_with('"') and s.ends_with('"') and s.length() >= 2:
return s.substr(1, s.length() - 2)
return s
func add_selector_in_editor_interface(_settings_service: EditorSettingsService):
_update_selector(_installations_found)
if _installations_found.is_empty() and _thread == null:
_thread = Thread.new()
_thread.start(_load_installations)
func _load_installations() -> void:
var array: Array = get_installations()
call_deferred("_on_installations_loaded", array)
func _on_installations_loaded(array: Array):
_installations_found = array
if _thread:
_thread.wait_to_finish()
_thread = null
_update_selector(_installations_found)
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE and _thread != null:
_thread.wait_to_finish()
func _update_selector(array: Array):
var name := "text_editor/external/editor"
var settings := EditorInterface.get_editor_settings()
if !(settings.has_setting(name)):
settings.set(name, 0)
var installations: Array = ["Custom"]
for element in array:
var display_name: String = element.get("display", "")
# Replace special characters that break PROPERTY_HINT_ENUM format
# Comma is the enum delimiter, colon is used for explicit value assignment
display_name = display_name.replace(",", "").replace(":", " -")
installations.append(display_name)
var options :String = ",".join(installations)
settings.add_property_info({
"name": name,
"type":TYPE_INT,
"hint":PROPERTY_HINT_ENUM,
"hint_string": options
})
# Connect to settings changes to update external editor path when selection changes
if not settings.settings_changed.is_connected(_on_selection_changed):
settings.settings_changed.connect(_on_selection_changed.bind())
func _on_selection_changed() -> void:
var name := "text_editor/external/editor"
var settings := EditorInterface.get_editor_settings()
var selected_index: int = settings.get_setting(name)
# Index 0 is "Custom", so user manages the path manually
if selected_index == 0:
return
# Map to actual installation (offset by 1 because of "Custom" at index 0)
var installation_index := selected_index - 1
var installations_array = _installations_found
if installation_index >= 0 and installation_index < installations_array.size():
var installation = installations_array[installation_index]
var new_path: String = installation.get("path", "")
if not new_path.is_empty():
EditorSettingsService.new().set_external_editor_path(settings, new_path)

View File

@@ -0,0 +1 @@
uid://cfsu1rbg0ypem

View File

@@ -0,0 +1,35 @@
@tool
extends RefCounted
class_name PresetApplier
var presets_path: String
func _init(p_path: String) -> void:
presets_path = p_path
func get_preset_key(is_active: bool) -> String:
return "on" if is_active else "off"
func apply_preset(editor_settings: EditorSettings, is_active: bool) -> void:
var data: Dictionary = JsonUtils.load_dict_from_file(presets_path)
if data.is_empty():
push_warning("Failed to load presets: %s" % presets_path)
return
var new_preset_key := get_preset_key(is_active)
var previous_preset_key := get_preset_key(not is_active)
if not data.has(new_preset_key):
push_warning("Preset '%s' not found in presets.json" % new_preset_key)
return
var new_preset := data[new_preset_key] as Dictionary
# Reset keys from previous preset that are missing in the new preset
if data.has(previous_preset_key):
var previous_preset := data[previous_preset_key] as Dictionary
for key in previous_preset:
if not new_preset.has(key):
editor_settings.set_setting(str(key), editor_settings.property_get_revert(str(key)))
# Apply the new preset
for key in new_preset:
editor_settings.set_setting(str(key), new_preset[key])

View File

@@ -0,0 +1 @@
uid://bjiaycxso8h8u

View File

@@ -0,0 +1,17 @@
@tool
extends RefCounted
class_name EditorSettingsService
func set_external_editor_path(editor_settings: EditorSettings, path: String) -> void:
editor_settings.set_setting("text_editor/external/exec_path", path)
func has_valid_external_editor_path(editor_settings: EditorSettings) -> bool:
var has_setting: bool = editor_settings.has_setting("text_editor/external/exec_path")
if not has_setting:
return false
var path : String = editor_settings.get_setting("text_editor/external/exec_path")
var exists := not path.is_empty() && (FileAccess.file_exists(path) || DirAccess.dir_exists_absolute(path))
return exists
func set_use_external_editor(editor_settings: EditorSettings, enabled: bool) -> void:
editor_settings.set_setting("text_editor/external/use_external_editor", enabled)

View File

@@ -0,0 +1 @@
uid://bdiu78ot0rrkc