Basic game template addon
This commit is contained in:
175
addons/maaacks_game_template/installer/copy_and_edit_files.gd
Normal file
175
addons/maaacks_game_template/installer/copy_and_edit_files.gd
Normal file
@@ -0,0 +1,175 @@
|
||||
@tool
|
||||
extends Node
|
||||
## Script for automatically copying Godot scenes and scripts without UIDs.
|
||||
|
||||
signal canceled
|
||||
signal completed(target_path : String)
|
||||
|
||||
const UID_PREG_MATCH = r'uid="uid:\/\/[0-9a-z]+" '
|
||||
const RUNNING_CHECK_DELAY : float = 0.25
|
||||
const RESAVING_DELAY : float = 1.0
|
||||
const RAW_COPY_EXTENSIONS : Array = ["gd", "md", "txt"]
|
||||
const OMIT_COPY_EXTENSIONS : Array = ["uid"]
|
||||
const REPLACE_CONTENT_EXTENSIONS : Array = ["gd", "tscn", "tres", "md"]
|
||||
|
||||
@onready var destination_dialog : FileDialog = $DestinationDialog
|
||||
|
||||
@export_dir var relative_path : String :
|
||||
set(value):
|
||||
relative_path = value
|
||||
if not relative_path.ends_with("/"):
|
||||
relative_path += "/"
|
||||
@export var replace_strings_map : Dictionary
|
||||
@export var visible : bool = true :
|
||||
set(value):
|
||||
visible = value
|
||||
if is_inside_tree():
|
||||
destination_dialog.visible = visible
|
||||
|
||||
func show() -> void:
|
||||
visible = true
|
||||
|
||||
func hide() -> void:
|
||||
visible = false
|
||||
|
||||
func close() -> void:
|
||||
queue_free()
|
||||
|
||||
func _remove_uids(content : String) -> String:
|
||||
var regex = RegEx.new()
|
||||
regex.compile(UID_PREG_MATCH)
|
||||
return regex.sub(content, "", true)
|
||||
|
||||
func _replace_paths(content : String, target_path : String) -> String:
|
||||
return content.replace(relative_path.trim_prefix("res://"), target_path.trim_prefix("res://"))
|
||||
|
||||
func _replace_strings(content : String) -> String:
|
||||
for key in replace_strings_map:
|
||||
var value : String = replace_strings_map[key]
|
||||
content = content.replace(key, value)
|
||||
return content
|
||||
|
||||
func _replace_content(content : String, target_path : String) -> String:
|
||||
var replaced_content : String
|
||||
replaced_content = _remove_uids(content)
|
||||
replaced_content = _replace_paths(replaced_content, target_path)
|
||||
replaced_content = _replace_strings(replaced_content)
|
||||
return replaced_content
|
||||
|
||||
func _replace_file_contents(file_path : String, target_path : String) -> void:
|
||||
var extension : String = file_path.get_extension()
|
||||
if extension not in REPLACE_CONTENT_EXTENSIONS:
|
||||
return
|
||||
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||
if file == null:
|
||||
push_error("plugin error - null file: `%s`" % file_path)
|
||||
return
|
||||
var original_content = file.get_as_text()
|
||||
file.close()
|
||||
var replaced_content := _replace_content(original_content, target_path)
|
||||
if replaced_content == original_content: return
|
||||
file = FileAccess.open(file_path, FileAccess.WRITE)
|
||||
file.store_string(replaced_content)
|
||||
file.close()
|
||||
|
||||
func _save_resource(resource_path : String, resource_destination : String, whitelisted_extensions : PackedStringArray = []) -> Error:
|
||||
var extension : String = resource_path.get_extension()
|
||||
if whitelisted_extensions.size() > 0:
|
||||
if not extension in whitelisted_extensions:
|
||||
return OK
|
||||
if extension == "import":
|
||||
# skip import files
|
||||
return OK
|
||||
var file_object = load(resource_path)
|
||||
if file_object is Resource:
|
||||
var possible_extensions = ResourceSaver.get_recognized_extensions(file_object)
|
||||
if possible_extensions.has(extension):
|
||||
return ResourceSaver.save(file_object, resource_destination, ResourceSaver.FLAG_CHANGE_PATH)
|
||||
else:
|
||||
return ERR_FILE_UNRECOGNIZED
|
||||
else:
|
||||
return ERR_FILE_UNRECOGNIZED
|
||||
return OK
|
||||
|
||||
func _raw_copy_file_path(file_path : String, destination_path : String) -> Error:
|
||||
var dir := DirAccess.open("res://")
|
||||
var error := dir.copy(file_path, destination_path)
|
||||
return error
|
||||
|
||||
func _copy_file_path(file_path : String, destination_path : String, target_path : String) -> Error:
|
||||
var error : Error
|
||||
if file_path.get_extension() in OMIT_COPY_EXTENSIONS:
|
||||
return error
|
||||
if file_path.get_extension() in RAW_COPY_EXTENSIONS:
|
||||
error = _raw_copy_file_path(file_path, destination_path)
|
||||
else:
|
||||
error = _save_resource(file_path, destination_path)
|
||||
if error == ERR_FILE_UNRECOGNIZED:
|
||||
error = _raw_copy_file_path(file_path, destination_path)
|
||||
if not error:
|
||||
_replace_file_contents(destination_path, target_path)
|
||||
return error
|
||||
|
||||
func _copy_directory_path(dir_path : String, target_path : String) -> void:
|
||||
if not dir_path.ends_with("/"):
|
||||
dir_path += "/"
|
||||
var dir = DirAccess.open(dir_path)
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
var error : Error
|
||||
while file_name != "" and error == 0:
|
||||
var file_relative_path = dir_path.trim_prefix(relative_path)
|
||||
var destination_path = target_path + file_relative_path + file_name
|
||||
var full_file_path = dir_path + file_name
|
||||
if dir.current_is_dir():
|
||||
if not dir.dir_exists(destination_path):
|
||||
error = dir.make_dir(destination_path)
|
||||
_copy_directory_path(full_file_path, target_path)
|
||||
else:
|
||||
error = _copy_file_path(full_file_path, destination_path, target_path)
|
||||
file_name = dir.get_next()
|
||||
if error:
|
||||
push_error("plugin error - copying path: %s" % error)
|
||||
else:
|
||||
push_error("plugin error - accessing path: %s" % dir_path)
|
||||
|
||||
func _complete(target_path : String) -> void:
|
||||
completed.emit(target_path)
|
||||
close()
|
||||
|
||||
func _wait_for_scan_and_complete(target_path : String) -> void:
|
||||
var timer: Timer = Timer.new()
|
||||
var callable := func():
|
||||
if EditorInterface.get_resource_filesystem().is_scanning(): return
|
||||
timer.stop()
|
||||
_complete(target_path)
|
||||
timer.queue_free()
|
||||
timer.timeout.connect(callable)
|
||||
add_child(timer)
|
||||
timer.start(RUNNING_CHECK_DELAY)
|
||||
|
||||
func _delayed_saving_and_next_prompt(target_path : String) -> void:
|
||||
var timer: Timer = Timer.new()
|
||||
var callable := func():
|
||||
timer.stop()
|
||||
EditorInterface.save_all_scenes()
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
_wait_for_scan_and_complete(target_path)
|
||||
timer.queue_free()
|
||||
timer.timeout.connect(callable)
|
||||
add_child(timer)
|
||||
timer.start(RESAVING_DELAY)
|
||||
|
||||
func _copy_to_directory(target_path : String) -> void:
|
||||
if not target_path.ends_with("/"):
|
||||
target_path += "/"
|
||||
_copy_directory_path(relative_path, target_path)
|
||||
_delayed_saving_and_next_prompt(target_path)
|
||||
|
||||
func _on_destination_dialog_dir_selected(dir):
|
||||
_copy_to_directory(dir)
|
||||
|
||||
func _on_destination_dialog_canceled():
|
||||
canceled.emit()
|
||||
close()
|
||||
Reference in New Issue
Block a user