mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 15:38:14 +00:00
added the game
This commit is contained in:
78
addons/mod_tool/interface/config_editor/ModConfigEditor.tscn
Normal file
78
addons/mod_tool/interface/config_editor/ModConfigEditor.tscn
Normal file
@@ -0,0 +1,78 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/mod_tool/interface/config_editor/json_editor.gd" type="Script" id=1]
|
||||
|
||||
[node name="Mod Config Editor" type="PanelContainer"]
|
||||
visible = false
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 24.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
offset_left = 7.0
|
||||
offset_top = 7.0
|
||||
offset_right = 1907.0
|
||||
offset_bottom = 891.0
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="VBox"]
|
||||
offset_right = 1900.0
|
||||
offset_bottom = 40.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBox/HBox"]
|
||||
offset_top = 13.0
|
||||
offset_right = 1533.0
|
||||
offset_bottom = 27.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default json for user mod configuration"
|
||||
|
||||
[node name="ErrorLabel" type="Label" parent="VBox/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 1537.0
|
||||
offset_top = 13.0
|
||||
offset_right = 1617.0
|
||||
offset_bottom = 27.0
|
||||
text = "JSON is valid"
|
||||
|
||||
[node name="ShouldValidate" type="CheckButton" parent="VBox/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 1621.0
|
||||
offset_right = 1697.0
|
||||
offset_bottom = 40.0
|
||||
pressed = true
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBox/HBox"]
|
||||
offset_left = 1701.0
|
||||
offset_right = 1705.0
|
||||
offset_bottom = 40.0
|
||||
|
||||
[node name="SaveConfig" type="Button" parent="VBox/HBox"]
|
||||
offset_left = 1709.0
|
||||
offset_right = 1900.0
|
||||
offset_bottom = 40.0
|
||||
text = "Save config to manifest.json"
|
||||
|
||||
[node name="ConfigEditor" type="TextEdit" parent="VBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_top = 44.0
|
||||
offset_right = 1900.0
|
||||
offset_bottom = 884.0
|
||||
size_flags_vertical = 3
|
||||
text = "{
|
||||
|
||||
}"
|
||||
highlight_current_line = true
|
||||
syntax_highlighter = true
|
||||
show_line_numbers = true
|
||||
fold_gutter = true
|
||||
highlight_all_occurrences = true
|
||||
smooth_scrolling = true
|
||||
hiding_enabled = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="ValidationDelay" type="Timer" parent="VBox/ConfigEditor"]
|
||||
one_shot = true
|
||||
|
||||
[connection signal="cursor_changed" from="VBox/ConfigEditor" to="VBox/ConfigEditor" method="_on_cursor_changed"]
|
||||
[connection signal="text_changed" from="VBox/ConfigEditor" to="VBox/ConfigEditor" method="_on_text_changed"]
|
||||
[connection signal="timeout" from="VBox/ConfigEditor/ValidationDelay" to="VBox/ConfigEditor" method="_on_ValidationDelay_timeout"]
|
226
addons/mod_tool/interface/config_editor/json_editor.gd
Normal file
226
addons/mod_tool/interface/config_editor/json_editor.gd
Normal file
@@ -0,0 +1,226 @@
|
||||
@tool
|
||||
extends TextEdit
|
||||
|
||||
signal discard_last_console_error
|
||||
|
||||
var base_theme: Theme: set = set_base_theme
|
||||
var editor_settings: EditorSettings: set = set_editor_settings
|
||||
|
||||
var last_text := ""
|
||||
var last_selection: TextSelection
|
||||
|
||||
var highlight_settings: PackedStringArray = [
|
||||
"string_color", "background_color", "line_number_color",
|
||||
"text_selected_color", "selection_color", "brace_mismatch_color",
|
||||
"current_line_color", "word_highlighted_color", "number_color",
|
||||
"code_folding_color", "symbol_color"
|
||||
]
|
||||
|
||||
var autobrace_pairs := {
|
||||
"(": ")",
|
||||
"{": "}",
|
||||
"[": "]",
|
||||
'"': '"',
|
||||
":": ",",
|
||||
}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
last_text = text
|
||||
$"%ShouldValidate".connect("pressed", Callable(self, "validate"))
|
||||
validate()
|
||||
|
||||
|
||||
func set_base_theme(p_base_theme: Theme) -> void:
|
||||
base_theme = p_base_theme
|
||||
add_theme_font_override("font", base_theme.get_font("source", "EditorFonts"))
|
||||
|
||||
|
||||
func set_editor_settings(p_editor_settings: EditorSettings) -> void:
|
||||
editor_settings = p_editor_settings
|
||||
|
||||
# TODO -> syntax_highlighter = get_setting_bool("text_editor/highlighting/syntax_highlighter")
|
||||
highlight_all_occurrences = get_setting_bool("text_editor/highlighting/highlight_all_occurrences")
|
||||
highlight_current_line = get_setting_bool("text_editor/highlighting/highlight_current_line")
|
||||
|
||||
draw_tabs = get_setting_bool("text_editor/indent/draw_tabs")
|
||||
draw_spaces = get_setting_bool("text_editor/indent/draw_spaces")
|
||||
|
||||
$ValidationDelay.wait_time = editor_settings.get_setting("text_editor/completion/idle_parse_delay")
|
||||
|
||||
# TODO -> add_color_region('"', '"', get_highlight_color("string_color"))
|
||||
for highlight in highlight_settings:
|
||||
add_theme_color_override(highlight, get_highlight_color(highlight))
|
||||
|
||||
|
||||
func get_highlight_color(name: String) -> Color:
|
||||
var color = editor_settings.get_setting("text_editor/highlighting/" + name)
|
||||
return color if color is Color else null
|
||||
|
||||
|
||||
func get_setting_bool(setting: String) -> bool:
|
||||
var is_set = editor_settings.get_setting(setting)
|
||||
return is_set if is_set is bool else false
|
||||
|
||||
|
||||
func validate() -> void:
|
||||
if not $"%ShouldValidate".pressed:
|
||||
$"%ErrorLabel".text = "Validation off"
|
||||
return
|
||||
|
||||
var test_json_conv = JSON.new()
|
||||
test_json_conv.parse(text)
|
||||
var parsed := test_json_conv.get_data()
|
||||
if not parsed.error == OK:
|
||||
$"%ErrorLabel".text = "Line %s: %s" % [parsed.error_line +1, parsed.error_string]
|
||||
emit_signal("discard_last_console_error")
|
||||
|
||||
return
|
||||
$"%ErrorLabel".text = "JSON is valid"
|
||||
|
||||
|
||||
func _on_cursor_changed() -> void:
|
||||
if get_selected_text().length() > 0:
|
||||
last_selection = TextSelection.from_text_edit(self)
|
||||
else:
|
||||
last_selection = null
|
||||
|
||||
|
||||
func _on_text_changed() -> void:
|
||||
$ValidationDelay.stop()
|
||||
$ValidationDelay.start()
|
||||
|
||||
if get_setting_bool("text_editor/completion/auto_brace_complete"):
|
||||
autobrace()
|
||||
|
||||
last_text = text
|
||||
|
||||
|
||||
func autobrace() -> void:
|
||||
var line := get_line(get_caret_line())
|
||||
|
||||
var char_before_cursor := ""
|
||||
if get_caret_column() > 0:
|
||||
char_before_cursor = line[get_caret_column()-1]
|
||||
|
||||
var char_after_cursor := ""
|
||||
if get_caret_column() < line.length():
|
||||
char_after_cursor = line[get_caret_column()]
|
||||
|
||||
# When deleting, also delete the autobraced character
|
||||
if Input.is_key_pressed(KEY_BACKSPACE):
|
||||
if char_after_cursor in autobrace_pairs.values():
|
||||
var deleted_character := first_different_character(text, last_text)
|
||||
if autobrace_pairs.has(deleted_character) and autobrace_pairs[deleted_character] == char_after_cursor:
|
||||
delete_character_after_cursor()
|
||||
|
||||
# If we encounter a closing brace, "skip" over it
|
||||
# Since the character is written already, just delete the next one
|
||||
elif is_matching_closing_brace(char_before_cursor, char_after_cursor):
|
||||
delete_character_after_cursor()
|
||||
|
||||
# If a character is in the autoclose dict, close it
|
||||
elif char_before_cursor in autobrace_pairs.keys():
|
||||
var closing_char: String = autobrace_pairs[char_before_cursor]
|
||||
var last_cursor_column := get_caret_column()
|
||||
|
||||
if not last_selection:
|
||||
insert_text_at_caret(closing_char)
|
||||
set_caret_column(last_cursor_column)
|
||||
return
|
||||
|
||||
# If there is a selection, surround that with the bracing characters
|
||||
# Pressing the alt key moves the selection left by one character
|
||||
if Input.is_key_pressed(KEY_ALT):
|
||||
if last_cursor_column == last_selection.from_col +1:
|
||||
# If selected right to left, it can be fixed by offsetting it right
|
||||
select(
|
||||
last_selection.from_line, last_selection.from_col +1,
|
||||
last_selection.to_line, last_selection.to_col +1
|
||||
)
|
||||
insert_text_at_caret(last_selection.enclosed_text + closing_char)
|
||||
set_caret_column(last_selection.to_col +1)
|
||||
else:
|
||||
# If selected left to right, something else goes wrong as well,
|
||||
# but it can be fixed by inserting the whole selection with braces
|
||||
# and removing the leftover trailing brace behind it afterwards
|
||||
insert_text_at_caret(char_before_cursor + last_selection.enclosed_text + closing_char)
|
||||
delete_character_after_cursor()
|
||||
set_caret_column(last_selection.to_col +1)
|
||||
else:
|
||||
insert_text_at_caret(last_selection.enclosed_text + closing_char)
|
||||
set_caret_column(last_selection.to_col +1)
|
||||
last_selection = null
|
||||
|
||||
|
||||
func is_matching_closing_brace(new_character: String, char_after_cursor: String) -> bool:
|
||||
if not new_character == char_after_cursor:
|
||||
return false
|
||||
|
||||
# Opening and closing brace are the same -> ""
|
||||
if char_after_cursor in autobrace_pairs.keys():
|
||||
return true
|
||||
|
||||
# Opening and closing brace are different -> ()
|
||||
if new_character in autobrace_pairs.values():
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func delete_character_after_cursor() -> void:
|
||||
var line_text := get_line(get_caret_line())
|
||||
var cursor_col := get_caret_column() +1
|
||||
var text_length := len(line_text)
|
||||
if cursor_col < 1 or cursor_col > text_length:
|
||||
return
|
||||
var left_text := line_text.substr(0, cursor_col - 1)
|
||||
var right_text := line_text.substr(cursor_col, text_length - cursor_col)
|
||||
set_line(get_caret_line(), left_text + right_text)
|
||||
set_caret_column(cursor_col - 1)
|
||||
|
||||
|
||||
func first_different_character(str1: String, str2: String) -> String:
|
||||
var len1 := str1.length()
|
||||
var len2 := str2.length()
|
||||
|
||||
if len1 == 0:
|
||||
return str2[0]
|
||||
if len2 == 0:
|
||||
return str1[0]
|
||||
|
||||
for i in min(len1, len2):
|
||||
if not str1[i] == str2[i]:
|
||||
return str2[i]
|
||||
return ""
|
||||
|
||||
|
||||
func _on_ValidationDelay_timeout() -> void:
|
||||
validate()
|
||||
|
||||
|
||||
class TextSelection:
|
||||
var enclosed_text: String
|
||||
var from_line: int
|
||||
var from_col: int
|
||||
var to_line: int
|
||||
var to_col: int
|
||||
|
||||
func _init(p_enclosed_text: String, p_from_line: int, p_from_col: int, p_to_line: int, p_to_col: int) -> void:
|
||||
enclosed_text = p_enclosed_text
|
||||
from_line = p_from_line
|
||||
from_col = p_from_col
|
||||
to_line = p_to_line
|
||||
to_col = p_to_col
|
||||
|
||||
|
||||
static func from_text_edit(text_edit: TextEdit) -> TextSelection:
|
||||
return TextSelection.new(
|
||||
text_edit.get_selection_text(),
|
||||
text_edit.get_selection_from_line(), text_edit.get_selection_from_column(),
|
||||
text_edit.get_selection_to_line(), text_edit.get_selection_to_column()
|
||||
)
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return "%s %s %s" % [ Vector2(from_line, from_col), enclosed_text, Vector2(to_line, to_col) ]
|
@@ -0,0 +1 @@
|
||||
uid://uu3ra0v727ro
|
153
addons/mod_tool/interface/create_mod/create_mod.gd
Normal file
153
addons/mod_tool/interface/create_mod/create_mod.gd
Normal file
@@ -0,0 +1,153 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
|
||||
signal mod_dir_created
|
||||
|
||||
const DIR_NAME_DEFAULT_TEMPLATE = "default"
|
||||
const DIR_NAME_MINIMAL_TEMPLATE = "minimal"
|
||||
|
||||
@onready var mod_tool_store: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
@onready var mod_namespace: ModToolInterfaceInputString = $"%Namespace"
|
||||
@onready var mod_name: ModToolInterfaceInputString = $"%ModName"
|
||||
@onready var mod_id: ModToolInterfaceInputString = $"%ModId"
|
||||
@onready var mod_template: ModToolInterfaceInputOptions = $"%ModTemplate"
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mod_namespace.show_error_if_not(false)
|
||||
mod_name.show_error_if_not(false)
|
||||
mod_id.show_error_if_not(false)
|
||||
|
||||
|
||||
func add_mod() -> void:
|
||||
# Validate mod-id
|
||||
if not mod_tool_store.manifest_data.is_mod_id_valid(mod_tool_store.name_mod_dir, mod_tool_store.name_mod_dir, "", true):
|
||||
ModToolUtils.output_error('Invalid name or namespace: "%s". You may only use letters, numbers, underscores and at least 3 characters for each.' % mod_tool_store.name_mod_dir)
|
||||
return
|
||||
|
||||
# Check if mod dir exists
|
||||
if not _ModLoaderFile.dir_exists(mod_tool_store.path_mod_dir):
|
||||
# If not - create it
|
||||
var success := ModToolUtils.make_dir_recursive(mod_tool_store.path_mod_dir)
|
||||
if not success:
|
||||
return
|
||||
|
||||
# Get Template files
|
||||
var template_paths := ModToolUtils.get_flat_view_dict(mod_tool_store.path_current_template_dir, "", [], false, true)
|
||||
|
||||
# Copy current selected template dir files and folders to res://mods-unpacked
|
||||
for path in template_paths:
|
||||
var template_local_path := path.trim_prefix(mod_tool_store.path_current_template_dir) as String
|
||||
if _ModLoaderFile.file_exists(path):
|
||||
ModToolUtils.file_copy(path, mod_tool_store.path_mod_dir.path_join(template_local_path))
|
||||
else:
|
||||
ModToolUtils.make_dir_recursive(mod_tool_store.path_mod_dir.path_join(template_local_path))
|
||||
|
||||
# Update FileSystem
|
||||
mod_tool_store.editor_file_system.scan()
|
||||
# Wait for the scan to finish
|
||||
await mod_tool_store.editor_file_system.filesystem_changed
|
||||
|
||||
# Navigate to the new mod dir in the FileSystem pannel
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(mod_tool_store.path_mod_dir.path_join("mod_main.gd"))
|
||||
|
||||
# Output info
|
||||
ModToolUtils.output_info("Added base mod files to " + mod_tool_store.path_mod_dir)
|
||||
|
||||
# Open mod_main.gd in the code editor
|
||||
var mod_main_script := load(mod_tool_store.path_mod_dir.path_join("mod_main.gd"))
|
||||
EditorInterface.edit_script(mod_main_script)
|
||||
EditorInterface.set_main_screen_editor("Script")
|
||||
|
||||
# Split the new mod id
|
||||
var name_mod_dir_split: Array = mod_tool_store.name_mod_dir.split("-")
|
||||
|
||||
# Update the namespace in the manifest
|
||||
mod_tool_store.manifest_data.mod_namespace = name_mod_dir_split[0]
|
||||
|
||||
# Update the mod name in the manifest
|
||||
mod_tool_store.manifest_data.name = name_mod_dir_split[1]
|
||||
|
||||
# Update manifest editor ui
|
||||
mod_tool_store.editor_plugin.tools_panel.manifest_editor.update_ui()
|
||||
|
||||
# Open manifest editor
|
||||
mod_tool_store.editor_plugin.tools_panel.show_manifest_editor()
|
||||
|
||||
# Save the manifest
|
||||
mod_tool_store.editor_plugin.tools_panel.manifest_editor.save_manifest()
|
||||
|
||||
else:
|
||||
# If so - show error and ask if user wants to connect with the mod instead
|
||||
ModToolUtils.output_error("Mod directory at %s already exists." % mod_tool_store.path_mod_dir)
|
||||
# TODO: Ask user to connect with the mod instead
|
||||
return
|
||||
|
||||
|
||||
func clear_mod_id_input() -> void:
|
||||
mod_id.input_text = ""
|
||||
|
||||
|
||||
func get_template_options() -> Array[String]:
|
||||
var mod_template_options: Array[String] = []
|
||||
|
||||
var template_dirs := _ModLoaderPath.get_dir_paths_in_dir(mod_tool_store.PATH_TEMPLATES_DIR)
|
||||
|
||||
# Add the default templates
|
||||
mod_template_options.push_back(DIR_NAME_DEFAULT_TEMPLATE)
|
||||
mod_template_options.push_back(DIR_NAME_MINIMAL_TEMPLATE)
|
||||
|
||||
for template_dir in template_dirs:
|
||||
var template_dir_name: String = template_dir.split("/")[-1]
|
||||
|
||||
# Skip if its one of the default templates
|
||||
if (
|
||||
template_dir_name == DIR_NAME_DEFAULT_TEMPLATE or
|
||||
template_dir_name == DIR_NAME_MINIMAL_TEMPLATE
|
||||
):
|
||||
continue
|
||||
|
||||
# Add all the custom templates
|
||||
mod_template_options.push_back(template_dir_name)
|
||||
|
||||
return mod_template_options
|
||||
|
||||
|
||||
func _on_Namespace_value_changed(new_value: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
input_node.validate(mod_tool_store.manifest_data.is_name_or_namespace_valid(new_value, true))
|
||||
mod_id.input_text = "%s-%s" % [mod_namespace.get_input_value(), mod_name.get_input_value()]
|
||||
|
||||
|
||||
func _on_ModName_value_changed(new_value: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
input_node.validate(mod_tool_store.manifest_data.is_name_or_namespace_valid(new_value, true))
|
||||
mod_id.input_text = "%s-%s" % [mod_namespace.get_input_value(), mod_name.get_input_value()]
|
||||
|
||||
|
||||
func _on_ModId_value_changed(new_value: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
input_node.validate(mod_tool_store.manifest_data.is_mod_id_valid(new_value, new_value, "", true))
|
||||
mod_tool_store.name_mod_dir = new_value
|
||||
|
||||
|
||||
func _on_btn_create_mod_pressed() -> void:
|
||||
add_mod()
|
||||
emit_signal("mod_dir_created")
|
||||
|
||||
|
||||
func _on_CreateMod_about_to_show() -> void:
|
||||
# Reset Inputs
|
||||
mod_namespace.input_text = ""
|
||||
mod_name.input_text = ""
|
||||
# Reset Template
|
||||
mod_tool_store.path_current_template_dir = mod_tool_store.PATH_TEMPLATES_DIR + "default"
|
||||
|
||||
# Get all Template options
|
||||
mod_template.input_options = get_template_options()
|
||||
|
||||
|
||||
func _on_ModTemplate_value_changed(new_value: String, input_node: ModToolInterfaceInputOptions) -> void:
|
||||
mod_tool_store.path_current_template_dir = mod_tool_store.PATH_TEMPLATES_DIR + new_value
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
hide()
|
1
addons/mod_tool/interface/create_mod/create_mod.gd.uid
Normal file
1
addons/mod_tool/interface/create_mod/create_mod.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bhfsuaepj5b4k
|
90
addons/mod_tool/interface/create_mod/create_mod.tscn
Normal file
90
addons/mod_tool/interface/create_mod/create_mod.tscn
Normal file
@@ -0,0 +1,90 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://glui2s46v4x4"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/create_mod/create_mod.gd" id="1"]
|
||||
[ext_resource type="PackedScene" path="res://addons/mod_tool/interface/global/input_string.tscn" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://dyunxqcmy4esi" path="res://addons/mod_tool/interface/global/input_options.tscn" id="3"]
|
||||
|
||||
[node name="CreateMod" type="Window"]
|
||||
position = Vector2i(0, 36)
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 25
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="Settings" type="VBoxContainer" parent="MarginContainer"]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.5
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="MarginContainer/Settings"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="MarginContainer/Settings/Scroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="Namespace" parent="MarginContainer/Settings/Scroll/VBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
input_placeholder = "Namespace"
|
||||
is_required = true
|
||||
key = "namespace"
|
||||
label_text = "Namespace ( Author Name )"
|
||||
|
||||
[node name="ModName" parent="MarginContainer/Settings/Scroll/VBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
input_placeholder = "ModName"
|
||||
is_required = true
|
||||
key = "name"
|
||||
label_text = "Mod Name"
|
||||
|
||||
[node name="ModId" parent="MarginContainer/Settings/Scroll/VBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
is_editable = false
|
||||
input_placeholder = "Namespace-ModName"
|
||||
is_required = true
|
||||
key = "mod_id"
|
||||
label_text = "Mod ID"
|
||||
|
||||
[node name="Space" type="Control" parent="MarginContainer/Settings/Scroll/VBox"]
|
||||
custom_minimum_size = Vector2(0, 15)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ModTemplate" parent="MarginContainer/Settings/Scroll/VBox" instance=ExtResource("3")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
input_options = Array[String]([])
|
||||
is_required = true
|
||||
key = "mod_template"
|
||||
label_text = "Template"
|
||||
|
||||
[node name="Buttons" type="PanelContainer" parent="MarginContainer/Settings"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="MarginContainer/Settings/Buttons"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="btn_create_mod" type="Button" parent="MarginContainer/Settings/Buttons/VBox"]
|
||||
layout_mode = 2
|
||||
text = "Create"
|
||||
|
||||
[connection signal="about_to_popup" from="." to="." method="_on_CreateMod_about_to_show"]
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
||||
[connection signal="value_changed" from="MarginContainer/Settings/Scroll/VBox/Namespace" to="." method="_on_Namespace_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/Settings/Scroll/VBox/ModName" to="." method="_on_ModName_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/Settings/Scroll/VBox/ModId" to="." method="_on_ModId_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/Settings/Scroll/VBox/ModTemplate" to="." method="_on_ModTemplate_value_changed"]
|
||||
[connection signal="pressed" from="MarginContainer/Settings/Buttons/VBox/btn_create_mod" to="." method="_on_btn_create_mod_pressed"]
|
@@ -0,0 +1,567 @@
|
||||
class_name FileSystemContextActions
|
||||
extends Control
|
||||
|
||||
|
||||
var mod_tool_store: ModToolStore
|
||||
|
||||
|
||||
class ContextActionOptions:
|
||||
extends Resource
|
||||
|
||||
var icon: StringName
|
||||
var title: String
|
||||
var meta_key: StringName
|
||||
var tooltip: String
|
||||
|
||||
func _init(_icon, _title, _meta_key, _tooltip) -> void:
|
||||
icon = _icon
|
||||
title = _title
|
||||
meta_key = _meta_key
|
||||
tooltip = _tooltip
|
||||
|
||||
|
||||
func _init(_mod_tool_store: ModToolStore, file_system_dock: FileSystemDock) -> void:
|
||||
mod_tool_store = _mod_tool_store
|
||||
connect_file_system_context_actions(file_system_dock)
|
||||
|
||||
|
||||
func connect_file_system_context_actions(file_system : FileSystemDock) -> void:
|
||||
var file_tree : Tree
|
||||
var file_list : ItemList
|
||||
|
||||
for node in file_system.get_children():
|
||||
if is_instance_of(node, SplitContainer):
|
||||
file_tree = node.get_child(0)
|
||||
file_list = node.get_child(1).get_child(1)
|
||||
break
|
||||
|
||||
for node in file_system.get_children():
|
||||
var context_menu : PopupMenu = node as PopupMenu
|
||||
if not context_menu:
|
||||
continue
|
||||
|
||||
context_menu.id_pressed.connect(_on_file_system_context_menu_pressed.bind(context_menu))
|
||||
|
||||
var signals := context_menu.get_signal_connection_list(&"id_pressed")
|
||||
if not signals.is_empty():
|
||||
match signals[0]["callable"].get_method():
|
||||
&"FileSystemDock::_tree_rmb_option":
|
||||
context_menu.about_to_popup.connect(_on_file_tree_context_actions_about_to_popup.bind(context_menu, file_tree))
|
||||
&"FileSystemDock::_file_list_rmb_option":
|
||||
context_menu.about_to_popup.connect(_on_file_list_context_actions_about_to_popup.bind(context_menu, file_tree))
|
||||
|
||||
|
||||
# Called every time the file system context actions pop up
|
||||
# Since they are dynamic, they are cleared every time and need to be refilled
|
||||
func add_custom_context_actions(context_menu: PopupMenu, file_paths: Array[String]) -> void:
|
||||
if file_paths.is_empty():
|
||||
return
|
||||
|
||||
var script_paths: Array[String] = []
|
||||
var asset_override_paths: Array[String] = []
|
||||
for file_path in file_paths:
|
||||
if DirAccess.dir_exists_absolute(file_path):
|
||||
continue
|
||||
|
||||
if FileAccess.file_exists(file_path):
|
||||
if file_path.ends_with(".gd"):
|
||||
script_paths.append(file_path)
|
||||
continue
|
||||
if file_path.ends_with(".tscn") or file_path.ends_with(".tres"):
|
||||
continue
|
||||
asset_override_paths.append(file_path)
|
||||
|
||||
if script_paths.size() > 0 or asset_override_paths.size() > 0:
|
||||
context_menu.add_separator()
|
||||
|
||||
if script_paths.size() > 0:
|
||||
add_script_extension_context_action(context_menu, script_paths)
|
||||
add_mod_hook_file_context_action(context_menu, script_paths)
|
||||
|
||||
var script_with_hook_count := ModToolUtils.check_for_hooked_script(script_paths, mod_tool_store)
|
||||
|
||||
if script_with_hook_count == script_paths.size():
|
||||
add_restore_context_action(context_menu, script_paths)
|
||||
elif script_with_hook_count > 0:
|
||||
add_restore_context_action(context_menu, script_paths)
|
||||
add_hooks_context_action(context_menu, script_paths)
|
||||
else:
|
||||
add_hooks_context_action(context_menu, script_paths)
|
||||
|
||||
if asset_override_paths.size() > 0:
|
||||
add_asset_override_context_action(context_menu, script_paths)
|
||||
|
||||
|
||||
func create_script_extension(file_path: String) -> String:
|
||||
if not mod_tool_store.name_mod_dir:
|
||||
ModToolUtils.output_error("Select an existing mod or create a new one to create script overrides")
|
||||
return ""
|
||||
|
||||
var file_directory := file_path.get_base_dir().trim_prefix("res://")
|
||||
var extension_directory: String = mod_tool_store.path_mod_dir.path_join("extensions").path_join(file_directory)
|
||||
ModToolUtils.make_dir_recursive(extension_directory)
|
||||
|
||||
var extension_path := extension_directory.path_join(file_path.get_file())
|
||||
var file := FileAccess.open(extension_path, FileAccess.WRITE)
|
||||
if not FileAccess.file_exists(extension_path):
|
||||
file.store_line('extends "%s"' % file_path)
|
||||
file.close()
|
||||
ModToolUtils.output_info('Created script extension of "%s" at path %s' % [file_path.get_file(), extension_path])
|
||||
|
||||
mod_tool_store.editor_file_system.scan()
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(extension_path)
|
||||
# Load the new extension script
|
||||
var extension_script: Script = load(extension_path)
|
||||
# Open the new extension script in the script editor
|
||||
EditorInterface.edit_script(extension_script)
|
||||
|
||||
return extension_path
|
||||
|
||||
|
||||
func create_mod_hook_file(file_path: String) -> String:
|
||||
if not mod_tool_store.name_mod_dir:
|
||||
ModToolUtils.output_error("Select an existing mod or create a new one to create script overrides")
|
||||
return ""
|
||||
|
||||
var file_directory := file_path.get_base_dir().trim_prefix("res://")
|
||||
var extension_directory: String = mod_tool_store.path_mod_dir.path_join("extensions").path_join(file_directory)
|
||||
ModToolUtils.make_dir_recursive(extension_directory)
|
||||
|
||||
var hook_file_name := "%s.hooks.%s" % [file_path.get_file().get_basename(), file_path.get_extension()]
|
||||
var extension_path := extension_directory.path_join(hook_file_name)
|
||||
print(extension_path)
|
||||
var file := FileAccess.open(extension_path, FileAccess.WRITE)
|
||||
if not FileAccess.file_exists(extension_path):
|
||||
file.store_line('extends Object')
|
||||
file.close()
|
||||
ModToolUtils.output_info('Created mod hook file for "%s" at path %s' % [file_path.get_file(), extension_path])
|
||||
|
||||
mod_tool_store.editor_file_system.scan()
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(extension_path)
|
||||
# Load the new extension script
|
||||
var extension_script: Script = load(extension_path)
|
||||
# Open the new extension script in the script editor
|
||||
EditorInterface.edit_script(extension_script)
|
||||
|
||||
return extension_path
|
||||
|
||||
|
||||
func add_script_extension_to_mod_main(extension_path: String) -> void:
|
||||
var main_script_path: String = mod_tool_store.path_mod_dir.path_join("mod_main.gd")
|
||||
|
||||
var file := FileAccess.open(main_script_path, FileAccess.READ_WRITE)
|
||||
if not file:
|
||||
ModToolUtils.output_error("Failed to open mod_main.gd with error \"%s\"" % error_string(FileAccess.get_open_error()))
|
||||
if not ModToolUtils.script_has_method(main_script_path, "install_script_extensions"):
|
||||
ModToolUtils.output_error('To automatically add new script extensions to "mod_main.gd", add "func install_script_extensions():" to it.')
|
||||
return
|
||||
|
||||
var file_content := file.get_as_text()
|
||||
|
||||
var index_find_from := file_content.find("func install_script_extensions")
|
||||
var mod_extensions_dir_path_index := file_content.find("extensions_dir_path", index_find_from)
|
||||
|
||||
# Construct the line required to install the extension. If the standard way is used and a
|
||||
# variable "extensions_dir_path" is found, use that variable in combination with path_join
|
||||
var extension_install_line := "\tModLoaderMod.install_script_extension(%s)\n"
|
||||
if mod_extensions_dir_path_index == -1:
|
||||
extension_install_line = extension_install_line % ModToolUtils.quote_string(extension_path)
|
||||
else:
|
||||
extension_path = extension_path.trim_prefix(mod_tool_store.path_mod_dir.path_join("extensions/"))
|
||||
extension_install_line = extension_install_line % "extensions_dir_path.path_join(%s)" % ModToolUtils.quote_string(extension_path)
|
||||
|
||||
# Check if that file was already used as script extension
|
||||
if extension_install_line.strip_edges() in file_content:
|
||||
return
|
||||
|
||||
var last_install_line_index := file_content.rfind("ModLoaderMod.install_script_extension")
|
||||
if last_install_line_index == -1:
|
||||
# If there is no ModLoaderMod.install_script_extension yet, put it at the end of install_script_extensions
|
||||
var insertion_index := ModToolUtils.get_index_at_method_end("install_script_extensions", file_content)
|
||||
file_content = file_content.insert(insertion_index, "\n" + extension_install_line)
|
||||
else:
|
||||
var last_install_line_end_index := file_content.find("\n", last_install_line_index)
|
||||
file_content = file_content.insert(last_install_line_end_index +1, extension_install_line)
|
||||
|
||||
file.store_string(file_content)
|
||||
|
||||
file.close()
|
||||
|
||||
ModToolUtils.output_info('Added script extension "%s" to mod "%s"' % [extension_path, main_script_path.get_base_dir().get_file()])
|
||||
|
||||
|
||||
func add_hook_file_to_mod_main(vanilla_path: String, extension_path: String) -> void:
|
||||
var main_script_path: String = mod_tool_store.path_mod_dir.path_join("mod_main.gd")
|
||||
|
||||
var file := FileAccess.open(main_script_path, FileAccess.READ_WRITE)
|
||||
if not file:
|
||||
ModToolUtils.output_error("Failed to open mod_main.gd with error \"%s\"" % error_string(FileAccess.get_open_error()))
|
||||
if not ModToolUtils.script_has_method(main_script_path, "install_script_hook_files"):
|
||||
ModToolUtils.output_error('To automatically add new script hook files to "mod_main.gd", add "func install_script_hook_files():" to it.')
|
||||
return
|
||||
|
||||
var file_content := file.get_as_text()
|
||||
|
||||
var index_find_from := file_content.find("func install_script_hook_files")
|
||||
var mod_extensions_dir_path_index := file_content.find("extensions_dir_path", index_find_from)
|
||||
|
||||
# Construct the line required to install the extension. If the standard way is used and a
|
||||
# variable "extensions_dir_path" is found, use that variable in combination with path_join
|
||||
var extension_install_line := "\tModLoaderMod.install_script_hooks(" + ModToolUtils.quote_string(vanilla_path) + ", %s)\n"
|
||||
if mod_extensions_dir_path_index == -1:
|
||||
extension_install_line = extension_install_line % ModToolUtils.quote_string(extension_path)
|
||||
else:
|
||||
extension_path = extension_path.trim_prefix(mod_tool_store.path_mod_dir.path_join("extensions/"))
|
||||
extension_install_line = extension_install_line % "extensions_dir_path.path_join(%s)" % ModToolUtils.quote_string(extension_path)
|
||||
|
||||
# Check if that file was already used as script extension
|
||||
if extension_install_line.strip_edges() in file_content:
|
||||
return
|
||||
|
||||
var last_install_line_index := file_content.rfind("ModLoaderMod.install_script_hooks")
|
||||
if last_install_line_index == -1:
|
||||
# If there is no ModLoaderMod.install_script_hooks yet, put it at the end of install_script_hook_files
|
||||
var insertion_index := ModToolUtils.get_index_at_method_end("install_script_hook_files", file_content)
|
||||
file_content = file_content.insert(insertion_index, "\n" + extension_install_line)
|
||||
else:
|
||||
var last_install_line_end_index := file_content.find("\n", last_install_line_index)
|
||||
file_content = file_content.insert(last_install_line_end_index +1, extension_install_line)
|
||||
|
||||
file.store_string(file_content)
|
||||
|
||||
file.close()
|
||||
|
||||
ModToolUtils.output_info('Added mod hooks file "%s" to mod "%s"' % [extension_path, main_script_path.get_base_dir().get_file()])
|
||||
|
||||
|
||||
func create_overwrite_asset(file_path: String) -> String:
|
||||
if not mod_tool_store.name_mod_dir:
|
||||
ModToolUtils.output_error("Select an existing mod or create a new one to overwrite assets")
|
||||
return ""
|
||||
|
||||
var file_directory := file_path.get_base_dir().trim_prefix("res://")
|
||||
var overwrite_directory: String = mod_tool_store.path_mod_dir.path_join("overwrites").path_join(file_directory)
|
||||
ModToolUtils.make_dir_recursive(overwrite_directory)
|
||||
|
||||
var overwrite_path := overwrite_directory.path_join(file_path.get_file())
|
||||
if not FileAccess.file_exists(overwrite_path):
|
||||
DirAccess.copy_absolute(file_path, overwrite_path)
|
||||
ModToolUtils.output_info('Copied asset "%s" as overwrite to path %s' % [file_path.get_file(), overwrite_path])
|
||||
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(overwrite_path)
|
||||
|
||||
return overwrite_path
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func add_asset_overwrite_to_overwrites(vanilla_asset_path: String, asset_path: String) -> void:
|
||||
var overwrites_script_path: String = mod_tool_store.path_mod_dir.path_join("overwrites.gd")
|
||||
var overwrites_script: GDScript
|
||||
var overwrites_script_new: Node
|
||||
var overwrites_script_syntax_tempalte := """extends Node
|
||||
|
||||
|
||||
var vanilla_file_paths: Array[String] = {%VANILLA_FILE_PATHS%}
|
||||
var overwrite_file_paths: Array[String] = {%OVERWRITE_FILE_PATHS%}
|
||||
|
||||
var overwrite_resources := []
|
||||
|
||||
|
||||
func _init():
|
||||
for i in overwrite_file_paths.size():
|
||||
var vanilla_path := vanilla_file_paths[i]
|
||||
var overwrite_path := overwrite_file_paths[i]
|
||||
|
||||
var overwrite_resource := load(overwrite_path)
|
||||
overwrite_resources.push_back(overwrite_resource)
|
||||
overwrite_resource.take_over_path(vanilla_path)
|
||||
"""
|
||||
|
||||
# overwrite.gd does not exist yet
|
||||
if not FileAccess.file_exists(overwrites_script_path):
|
||||
overwrites_script = GDScript.new()
|
||||
overwrites_script.source_code = overwrites_script_syntax_tempalte.format({
|
||||
"%VANILLA_FILE_PATHS%": "[]",
|
||||
"%OVERWRITE_FILE_PATHS%": "[]",
|
||||
})
|
||||
var success := ResourceSaver.save(overwrites_script, overwrites_script_path)
|
||||
if not success == OK:
|
||||
ModToolUtils.output_error("Failed to save overwrite.gd with error \"%s\"" % error_string(FileAccess.get_open_error()))
|
||||
|
||||
overwrites_script = load(overwrites_script_path)
|
||||
overwrites_script_new = overwrites_script.new()
|
||||
|
||||
# Check if the overwrites script has the neccessary props
|
||||
if (
|
||||
not ModToolUtils.script_has_method(overwrites_script_path, "vanilla_file_paths") or
|
||||
not ModToolUtils.script_has_method(overwrites_script_path, "overwrite_file_paths")
|
||||
):
|
||||
ModToolUtils.output_error("The 'overwrites.gd' file has an unexpected format. To proceed, please delete the existing 'overwrites.gd' file and allow the tool to regenerate it automatically.")
|
||||
return
|
||||
|
||||
# Check if that asset is already being overwritten
|
||||
if asset_path in overwrites_script_new.overwrite_file_paths:
|
||||
return
|
||||
|
||||
overwrites_script_new.vanilla_file_paths.push_back(vanilla_asset_path)
|
||||
overwrites_script_new.overwrite_file_paths.push_back(asset_path)
|
||||
|
||||
overwrites_script.source_code = overwrites_script_syntax_tempalte.format({
|
||||
"%VANILLA_FILE_PATHS%": JSON.stringify(overwrites_script_new.vanilla_file_paths, "\t"),
|
||||
"%OVERWRITE_FILE_PATHS%": JSON.stringify(overwrites_script_new.overwrite_file_paths, "\t"),
|
||||
})
|
||||
|
||||
ResourceSaver.save(overwrites_script)
|
||||
|
||||
overwrites_script_new.free()
|
||||
|
||||
# Open the overwrites script in the script editor
|
||||
EditorInterface.edit_script(overwrites_script)
|
||||
|
||||
ModToolUtils.output_info('Added asset overwrite "%s" to mod "%s"' % [asset_path, overwrites_script_path.get_base_dir().get_file()])
|
||||
|
||||
|
||||
func add_context_action(context_menu: PopupMenu, script_paths: Array[String], options: ContextActionOptions) -> void:
|
||||
context_menu.add_icon_item(
|
||||
mod_tool_store.editor_base_control.get_theme_icon(options.icon, &"EditorIcons"),
|
||||
"ModTool: %s" % options.title + ("s (%s)" % script_paths.size() if script_paths.size() > 1 else "")
|
||||
)
|
||||
context_menu.set_item_metadata(
|
||||
context_menu.get_item_count() -1,
|
||||
{ options.meta_key: script_paths }
|
||||
)
|
||||
context_menu.set_item_tooltip(
|
||||
context_menu.get_item_count() -1,
|
||||
"%s: \n%s" %
|
||||
[options.tooltip, str(script_paths).trim_prefix("[").trim_suffix("]").replace(", ", "\n")]
|
||||
)
|
||||
|
||||
|
||||
func add_script_extension_context_action(context_menu: PopupMenu, script_paths: Array[String]) -> void:
|
||||
add_context_action(
|
||||
context_menu,
|
||||
script_paths,
|
||||
ContextActionOptions.new(
|
||||
&"ScriptExtend",
|
||||
"Create Script Extension",
|
||||
&"mod_tool_script_paths",
|
||||
"Will add extensions for"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func add_mod_hook_file_context_action(context_menu: PopupMenu, script_paths: Array[String]) -> void:
|
||||
add_context_action(
|
||||
context_menu,
|
||||
script_paths,
|
||||
ContextActionOptions.new(
|
||||
&"ScriptExtend",
|
||||
"Create Mod Hook File",
|
||||
&"mod_tool_mod_hook_file_paths",
|
||||
"Will add mod hook files for"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func add_restore_context_action(context_menu: PopupMenu, script_paths: Array[String]) -> void:
|
||||
var script_paths_to_restore: Array[String] = script_paths.filter(
|
||||
func(script_path): return mod_tool_store.hooked_scripts.has(script_path)
|
||||
)
|
||||
|
||||
add_context_action(
|
||||
context_menu,
|
||||
script_paths_to_restore,
|
||||
ContextActionOptions.new(
|
||||
&"UndoRedo",
|
||||
"Restore script to unhooked version",
|
||||
&"mod_tool_restore_script_paths",
|
||||
"Will restore the non hooked script for"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func add_asset_override_context_action(context_menu: PopupMenu, script_paths: Array[String]) -> void:
|
||||
add_context_action(
|
||||
context_menu,
|
||||
script_paths,
|
||||
ContextActionOptions.new(
|
||||
&"Override",
|
||||
"Create Asset Overwrite",
|
||||
&"mod_tool_override_paths",
|
||||
"Will overwrite assets"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func add_hooks_context_action(context_menu: PopupMenu, script_paths: Array[String]) -> void:
|
||||
var script_paths_to_add_hooks: Array[String] = script_paths.filter(
|
||||
func(script_path): return not mod_tool_store.hooked_scripts.has(script_path)
|
||||
)
|
||||
|
||||
add_context_action(
|
||||
context_menu,
|
||||
script_paths_to_add_hooks,
|
||||
ContextActionOptions.new(
|
||||
&"ShaderGlobalsOverride",
|
||||
"Convert script to hooked version",
|
||||
&"mod_tool_hook_script_paths",
|
||||
"Will add mod hooks for"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func handle_script_extension_creation(metadata: Dictionary) -> void:
|
||||
var file_paths = metadata.mod_tool_script_paths
|
||||
var mod_main_path := mod_tool_store.path_mod_dir.path_join("mod_main.gd")
|
||||
|
||||
for file_path in file_paths:
|
||||
var extension_path := create_script_extension(file_path)
|
||||
if extension_path:
|
||||
add_script_extension_to_mod_main(extension_path)
|
||||
|
||||
# We navigate to the created script extension in `create_script_extension()`, so we should never
|
||||
# instantly refresh the mod main script here. If we call `ModToolUtils.reload_script()`
|
||||
# after this, it's possible that the `mod_main` content gets copied into the
|
||||
# newly created extension script. If that script is then saved, we
|
||||
# unintentionally overwrite the original script content.
|
||||
mod_tool_store.pending_reloads.push_back(mod_main_path)
|
||||
|
||||
#Switch to the script screen
|
||||
EditorInterface.set_main_screen_editor("Script")
|
||||
|
||||
|
||||
func handle_mod_hook_file_creation(metadata: Dictionary) -> void:
|
||||
var file_paths = metadata.mod_tool_mod_hook_file_paths
|
||||
var mod_main_path := mod_tool_store.path_mod_dir.path_join("mod_main.gd")
|
||||
|
||||
for file_path in file_paths:
|
||||
var extension_path := create_mod_hook_file(file_path)
|
||||
if extension_path:
|
||||
add_hook_file_to_mod_main(file_path, extension_path)
|
||||
|
||||
# We navigate to the created script extension in `create_script_extension()`, so we should never
|
||||
# instantly refresh the mod main script here. If we call `ModToolUtils.reload_script()`
|
||||
# after this, it's possible that the `mod_main` content gets copied into the
|
||||
# newly created extension script. If that script is then saved, we
|
||||
# unintentionally overwrite the original script content.
|
||||
mod_tool_store.pending_reloads.push_back(mod_main_path)
|
||||
|
||||
#Switch to the script screen
|
||||
EditorInterface.set_main_screen_editor("Script")
|
||||
|
||||
|
||||
func handle_override_creation(metadata: Dictionary) -> void:
|
||||
var file_paths: Array[String] = metadata.mod_tool_override_paths
|
||||
var current_script: GDScript
|
||||
var overwrites_path := mod_tool_store.path_mod_dir.path_join("overwrites.gd")
|
||||
|
||||
for file_path in file_paths:
|
||||
var asset_path := create_overwrite_asset(file_path)
|
||||
if asset_path:
|
||||
add_asset_overwrite_to_overwrites(file_path, asset_path)
|
||||
|
||||
current_script = EditorInterface.get_script_editor().get_current_script()
|
||||
|
||||
mod_tool_store.pending_reloads.push_back(overwrites_path)
|
||||
|
||||
if current_script.resource_path == overwrites_path:
|
||||
ModToolUtils.reload_script(current_script, mod_tool_store)
|
||||
|
||||
#Switch to the script screen
|
||||
EditorInterface.set_main_screen_editor("Script")
|
||||
|
||||
|
||||
func handle_mod_hook_creation(metadata: Dictionary) -> void:
|
||||
var file_paths: Array[String] = metadata.mod_tool_hook_script_paths
|
||||
var current_script: GDScript
|
||||
|
||||
for file_path in file_paths:
|
||||
var error := ModToolHookGen.transform_one(file_path, mod_tool_store)
|
||||
|
||||
if not error == OK:
|
||||
ModToolUtils.output_error("Error creating mod hooks for script at path: \"%s\" error: \"%s\" " % [file_path, error_string(error)])
|
||||
return
|
||||
|
||||
mod_tool_store.pending_reloads.push_back(file_path)
|
||||
current_script = EditorInterface.get_script_editor().get_current_script()
|
||||
|
||||
if current_script.resource_path == file_path:
|
||||
ModToolUtils.reload_script(current_script, mod_tool_store)
|
||||
|
||||
ModToolUtils.output_info("Mod Hooks created for script at path: \"%s\"" % file_path)
|
||||
|
||||
|
||||
func handle_mod_hook_restore(metadata: Dictionary) -> void:
|
||||
var file_paths: Array[String] = metadata.mod_tool_restore_script_paths
|
||||
var current_script: GDScript
|
||||
|
||||
for file_path in file_paths:
|
||||
var error := ModToolHookGen.restore(file_path, mod_tool_store)
|
||||
|
||||
if not error == OK:
|
||||
ModToolUtils.output_error("ERROR: Restoring script: \"%s\" with error: \"%s\"" % [file_path, error_string(error)])
|
||||
return
|
||||
|
||||
mod_tool_store.pending_reloads.push_back(file_path)
|
||||
current_script = EditorInterface.get_script_editor().get_current_script()
|
||||
|
||||
if current_script.resource_path == file_path:
|
||||
ModToolUtils.reload_script(current_script, mod_tool_store)
|
||||
|
||||
|
||||
func _on_file_tree_context_actions_about_to_popup(context_menu: PopupMenu, tree: Tree) -> void:
|
||||
var selected := tree.get_next_selected(null)
|
||||
if not selected: # Empty space was clicked
|
||||
return
|
||||
|
||||
# multiselection
|
||||
var file_paths: Array[String] = []
|
||||
while selected:
|
||||
var file_path = selected.get_metadata(0)
|
||||
if file_path is String:
|
||||
file_paths.append(file_path)
|
||||
selected = tree.get_next_selected(selected)
|
||||
|
||||
add_custom_context_actions(context_menu, file_paths)
|
||||
|
||||
|
||||
func _on_file_list_context_actions_about_to_popup(context_menu: PopupMenu, list: ItemList) -> void:
|
||||
if not list.get_selected_items().size() > 0: # Empty space was clicked
|
||||
return
|
||||
|
||||
var file_paths := []
|
||||
for item_index in list.get_selected_items():
|
||||
var file_path = list.get_item_metadata(item_index)
|
||||
if file_path is String:
|
||||
file_paths.append(file_path)
|
||||
|
||||
add_custom_context_actions(context_menu, file_paths)
|
||||
|
||||
|
||||
func _on_file_system_context_menu_pressed(id: int, context_menu: PopupMenu) -> void:
|
||||
var file_paths: PackedStringArray
|
||||
var metadata = context_menu.get_item_metadata(id)
|
||||
var current_script: GDScript
|
||||
|
||||
# Ensure that the metadata is actually set by the ModTool
|
||||
# Since id and index of the item can always change
|
||||
if metadata is Dictionary and metadata.has("mod_tool_script_paths"):
|
||||
handle_script_extension_creation(metadata)
|
||||
|
||||
if metadata is Dictionary and metadata.has("mod_tool_override_paths"):
|
||||
handle_override_creation(metadata)
|
||||
|
||||
if metadata is Dictionary and metadata.has("mod_tool_mod_hook_file_paths"):
|
||||
handle_mod_hook_file_creation(metadata)
|
||||
|
||||
if metadata is Dictionary and metadata.has("mod_tool_hook_script_paths"):
|
||||
handle_mod_hook_creation(metadata)
|
||||
|
||||
if metadata is Dictionary and metadata.has("mod_tool_restore_script_paths"):
|
||||
handle_mod_hook_restore(metadata)
|
@@ -0,0 +1 @@
|
||||
uid://83v2dur6o2g6
|
1
addons/mod_tool/interface/global/button_with_hint.gd.uid
Normal file
1
addons/mod_tool/interface/global/button_with_hint.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1yeemsw0ujl7
|
@@ -0,0 +1,35 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
|
||||
signal dir_selected(dir_path)
|
||||
|
||||
@onready var directory_list: VBoxContainer = $"%DirectoryList"
|
||||
|
||||
|
||||
func generate_dir_buttons(dir_path: String) -> void:
|
||||
clear_directory_list()
|
||||
var dir_paths := _ModLoaderPath.get_dir_paths_in_dir(dir_path)
|
||||
|
||||
for path in dir_paths:
|
||||
var dir_name: String = path.split('/')[-1]
|
||||
|
||||
var dir_btn := Button.new()
|
||||
dir_btn.text = dir_name
|
||||
|
||||
directory_list.add_child(dir_btn)
|
||||
dir_btn.pressed.connect(_on_dir_btn_dir_selected.bind(path))
|
||||
|
||||
|
||||
func clear_directory_list() -> void:
|
||||
for child in directory_list.get_children():
|
||||
directory_list.remove_child(child)
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func _on_dir_btn_dir_selected(path: String) -> void:
|
||||
dir_selected.emit(path)
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
hide()
|
@@ -0,0 +1 @@
|
||||
uid://dyxgsmyqpv8e2
|
@@ -0,0 +1,31 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://du17jjwqtopix"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/global/directory_selection/select_directory.gd" id="1"]
|
||||
|
||||
[node name="SelectDirectory" type="Window"]
|
||||
initial_position = 2
|
||||
size = Vector2i(400, 250)
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 25
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="DirectoryList" type="VBoxContainer" parent="MarginContainer/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
66
addons/mod_tool/interface/global/input.gd
Normal file
66
addons/mod_tool/interface/global/input.gd
Normal file
@@ -0,0 +1,66 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceInput
|
||||
extends HBoxContainer
|
||||
|
||||
|
||||
signal value_changed(new_value, input_node)
|
||||
|
||||
@export var is_required: bool:
|
||||
set = set_is_required
|
||||
@export var key: String
|
||||
@export var label_text: String:
|
||||
set = set_label_text
|
||||
@export var editor_icon_name: String = "NodeWarning"
|
||||
@export var hint_text: String:
|
||||
set = set_hint_text
|
||||
|
||||
var is_valid := true: set = set_is_valid
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Set up warning icons to show if a field is invalid
|
||||
set_editor_icon(editor_icon_name)
|
||||
|
||||
|
||||
func set_is_required(required: bool) -> void:
|
||||
is_required = required
|
||||
set_label_text(label_text)
|
||||
|
||||
|
||||
func set_is_valid(new_is_valid: bool) -> void:
|
||||
is_valid = new_is_valid
|
||||
show_error_if_not(is_valid)
|
||||
|
||||
|
||||
func set_label_text(new_text: String) -> void:
|
||||
label_text = new_text
|
||||
$Label.text = new_text if is_required else new_text + " (optional)"
|
||||
|
||||
|
||||
func set_hint_text(new_text: String) -> void:
|
||||
hint_text = new_text
|
||||
tooltip_text = new_text
|
||||
mouse_default_cursor_shape = CURSOR_ARROW if new_text == "" else CURSOR_HELP
|
||||
|
||||
|
||||
func set_editor_icon(icon_name: String) -> void:
|
||||
var mod_tool_store: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
|
||||
if icon_name and mod_tool_store:
|
||||
set_error_icon(mod_tool_store.editor_base_control.get_theme_icon(icon_name, "EditorIcons"))
|
||||
|
||||
|
||||
func set_error_icon(icon: Texture2D) -> void:
|
||||
$"%ErrorIcon".texture = icon
|
||||
|
||||
|
||||
func show_error_if_not(condition: bool) -> void:
|
||||
if not condition:
|
||||
$"%ErrorIcon".self_modulate = Color.WHITE
|
||||
else:
|
||||
$"%ErrorIcon".self_modulate = Color.TRANSPARENT
|
||||
|
||||
|
||||
func validate(_condition: bool) -> bool:
|
||||
printerr("Implement a validation method")
|
||||
return false
|
1
addons/mod_tool/interface/global/input.gd.uid
Normal file
1
addons/mod_tool/interface/global/input.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://rtvobwnj2vbx
|
42
addons/mod_tool/interface/global/input_options.gd
Normal file
42
addons/mod_tool/interface/global/input_options.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceInputOptions
|
||||
extends ModToolInterfaceInput
|
||||
|
||||
|
||||
@export var input_options: Array[String]: set = set_input_options
|
||||
|
||||
|
||||
func set_input_options(new_options: Array[String]) -> void:
|
||||
input_options = new_options
|
||||
var input: OptionButton = get_node_or_null("%Input") as OptionButton
|
||||
if not input or new_options.is_empty(): return # node can't be found directly after reloading the plugin
|
||||
|
||||
input.clear()
|
||||
for option in input_options:
|
||||
input.add_item(option)
|
||||
input.select(0)
|
||||
|
||||
|
||||
func get_input_value() -> int:
|
||||
return ($"%Input" as OptionButton).get_selected_id()
|
||||
|
||||
|
||||
func get_input_string() -> String:
|
||||
if get_input_value() == -1:
|
||||
return ""
|
||||
return input_options[get_input_value()]
|
||||
|
||||
|
||||
func validate(condition: bool) -> bool:
|
||||
# Check if input is required and empty
|
||||
if is_required and get_input_value() == -1:
|
||||
is_valid = false
|
||||
return false
|
||||
|
||||
# Invalidate field if the condition is not met
|
||||
is_valid = condition
|
||||
return is_valid
|
||||
|
||||
|
||||
func _on_Input_item_selected(index: int) -> void:
|
||||
emit_signal("value_changed", get_input_string(), self)
|
1
addons/mod_tool/interface/global/input_options.gd.uid
Normal file
1
addons/mod_tool/interface/global/input_options.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8rtbm4ypixcs
|
43
addons/mod_tool/interface/global/input_options.tscn
Normal file
43
addons/mod_tool/interface/global/input_options.tscn
Normal file
@@ -0,0 +1,43 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://dyunxqcmy4esi"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/global/input_options.gd" id="1"]
|
||||
|
||||
[sub_resource type="Image" id="Image_43403"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_xi7aw"]
|
||||
image = SubResource("Image_43403")
|
||||
|
||||
[node name="InputOptions" type="HBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 32.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = " (optional)"
|
||||
clip_text = true
|
||||
|
||||
[node name="ErrorIcon" type="TextureRect" parent="."]
|
||||
unique_name_in_owner = true
|
||||
self_modulate = Color(1, 1, 1, 0)
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
texture = SubResource("ImageTexture_xi7aw")
|
||||
expand_mode = 1
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="Input" type="OptionButton" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[connection signal="item_selected" from="Input" to="." method="_on_Input_item_selected"]
|
63
addons/mod_tool/interface/global/input_string.gd
Normal file
63
addons/mod_tool/interface/global/input_string.gd
Normal file
@@ -0,0 +1,63 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceInputString
|
||||
extends ModToolInterfaceInput
|
||||
|
||||
|
||||
@export var is_editable := true: set = set_is_editable
|
||||
@export var input_text: String: set = set_input_text
|
||||
@export var input_placeholder: String: set = set_input_placeholder
|
||||
|
||||
|
||||
func set_input_text(new_text: String) -> void:
|
||||
input_text = new_text
|
||||
$"%Input".text = new_text
|
||||
emit_signal("value_changed", new_text, self)
|
||||
|
||||
|
||||
func set_input_placeholder(new_text: String) -> void:
|
||||
input_placeholder = new_text
|
||||
$"%Input".placeholder_text = new_text
|
||||
|
||||
|
||||
func set_is_editable(new_is_editable: bool) -> void:
|
||||
is_editable = new_is_editable
|
||||
$"%Input".editable = new_is_editable
|
||||
|
||||
|
||||
func get_input_value() -> String:
|
||||
return $"%Input".text.strip_edges()
|
||||
|
||||
|
||||
# Gets the values of a comma separated string as an Array,
|
||||
# strips any white space contained in this values.
|
||||
func get_input_as_array_from_comma_separated_string() -> Array:
|
||||
var string_split := get_input_value().split(",", false)
|
||||
var array := []
|
||||
|
||||
for string in string_split:
|
||||
array.append(string.strip_edges())
|
||||
|
||||
return array
|
||||
|
||||
|
||||
func validate(condition: bool) -> bool:
|
||||
# Check if input is required and empty
|
||||
if is_required and get_input_value() == "":
|
||||
is_valid = false
|
||||
return false
|
||||
|
||||
# Invalidate field if the condition is not met
|
||||
self.is_valid = condition
|
||||
return is_valid
|
||||
|
||||
|
||||
func emit_value_changed() -> void:
|
||||
emit_signal("value_changed", get_input_value(), self)
|
||||
|
||||
|
||||
func _on_Input_text_changed(new_text: String) -> void:
|
||||
emit_value_changed()
|
||||
|
||||
|
||||
func _on_Mutiline_Input_text_changed() -> void:
|
||||
emit_value_changed()
|
1
addons/mod_tool/interface/global/input_string.gd.uid
Normal file
1
addons/mod_tool/interface/global/input_string.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://itwpownujndt
|
43
addons/mod_tool/interface/global/input_string.tscn
Normal file
43
addons/mod_tool/interface/global/input_string.tscn
Normal file
@@ -0,0 +1,43 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://icwo58h0rdb5"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/global/input_string.gd" id="1"]
|
||||
|
||||
[sub_resource type="Image" id="Image_6wlu1"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 43, 6, 255, 250, 10, 183, 255, 252, 10, 179, 255, 255, 0, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 14, 123, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 115, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 8, 30, 255, 250, 10, 245, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 242, 255, 255, 19, 26, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 174, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 12, 166, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 252, 13, 71, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 254, 10, 65, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 43, 6, 255, 252, 10, 218, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 213, 255, 255, 0, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 14, 123, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 115, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 8, 30, 255, 250, 10, 245, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 242, 255, 255, 19, 26, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 174, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 12, 166, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 252, 13, 71, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 254, 10, 65, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 213, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 207, 255, 255, 255, 0, 255, 255, 255, 0, 255, 250, 12, 191, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 250, 10, 255, 255, 252, 10, 187, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_5jdd3"]
|
||||
image = SubResource("Image_6wlu1")
|
||||
|
||||
[node name="InputString" type="HBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 24.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = " (optional)"
|
||||
clip_text = true
|
||||
|
||||
[node name="ErrorIcon" type="TextureRect" parent="."]
|
||||
unique_name_in_owner = true
|
||||
self_modulate = Color(1, 1, 1, 0)
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
texture = SubResource("ImageTexture_5jdd3")
|
||||
expand_mode = 1
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="Input" type="LineEdit" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[connection signal="text_changed" from="Input" to="." method="_on_Input_text_changed"]
|
70
addons/mod_tool/interface/global/input_string_multiline.tscn
Normal file
70
addons/mod_tool/interface/global/input_string_multiline.tscn
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceInputStringWithButton
|
||||
extends ModToolInterfaceInputString
|
||||
|
||||
signal button_pressed
|
@@ -0,0 +1 @@
|
||||
uid://dkdwnp05n1vy6
|
@@ -0,0 +1,65 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/mod_tool/interface/global/input_string_with_button.gd" type="Script" id=1]
|
||||
|
||||
[sub_resource type="Image" id=3]
|
||||
data = {
|
||||
"data": PackedByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 204, 51, 0, 255, 220, 100, 0, 255, 219, 100, 0, 255, 204, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 204, 51, 0, 255, 204, 51, 5, 255, 220, 100, 178, 255, 219, 100, 182, 255, 204, 51, 5, 255, 204, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 217, 98, 0, 255, 219, 99, 115, 255, 221, 101, 255, 255, 221, 101, 255, 255, 219, 99, 115, 255, 216, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 215, 98, 0, 255, 215, 98, 26, 255, 220, 100, 241, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 100, 241, 255, 214, 91, 25, 255, 214, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 221, 98, 0, 255, 221, 100, 165, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 100, 165, 255, 221, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 206, 80, 0, 255, 221, 97, 60, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 97, 60, 255, 206, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 191, 63, 0, 255, 191, 63, 4, 255, 221, 100, 210, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 100, 209, 255, 191, 63, 4, 255, 191, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 219, 98, 0, 255, 220, 99, 110, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 99, 110, 255, 219, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 218, 97, 0, 255, 218, 97, 21, 255, 220, 100, 239, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 100, 239, 255, 218, 97, 21, 255, 218, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 218, 100, 0, 255, 219, 100, 160, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 219, 100, 160, 255, 218, 100, 0, 0, 0, 0, 0, 255, 218, 100, 0, 255, 218, 100, 56, 255, 220, 100, 254, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 100, 254, 255, 218, 100, 56, 255, 218, 100, 0, 255, 220, 100, 0, 255, 220, 100, 193, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 220, 100, 193, 255, 220, 100, 0, 255, 221, 100, 0, 255, 221, 100, 165, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 101, 255, 255, 221, 100, 165, 255, 221, 100, 0, 0, 0, 0, 0, 255, 221, 100, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 101, 0, 255, 221, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id=2]
|
||||
flags = 0
|
||||
flags = 0
|
||||
image = SubResource( 3 )
|
||||
size = Vector2( 16, 16 )
|
||||
|
||||
[node name="InputStringWithButton" type="HBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 24.0
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
offset_top = 9.0
|
||||
offset_right = 492.0
|
||||
offset_bottom = 23.0
|
||||
size_flags_horizontal = 3
|
||||
text = " (optional)"
|
||||
clip_text = true
|
||||
|
||||
[node name="ErrorIcon" type="TextureRect" parent="."]
|
||||
unique_name_in_owner = true
|
||||
self_modulate = Color( 1, 1, 1, 0 )
|
||||
offset_left = 496.0
|
||||
offset_right = 528.0
|
||||
offset_bottom = 32.0
|
||||
custom_minimum_size = Vector2( 32, 32 )
|
||||
texture = SubResource( 2 )
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
offset_left = 532.0
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Input" type="LineEdit" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 456.0
|
||||
offset_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
placeholder_alpha = 0.5
|
||||
|
||||
[node name="Button" type="Button" parent="HBoxContainer"]
|
||||
offset_left = 460.0
|
||||
offset_right = 492.0
|
||||
offset_bottom = 32.0
|
||||
size_flags_vertical = 3
|
||||
text = " ... "
|
||||
|
||||
[connection signal="text_changed" from="HBoxContainer/Input" to="." method="_on_Input_text_changed"]
|
||||
[connection signal="pressed" from="HBoxContainer/Button" to="." method="emit_signal" binds= [ "button_pressed" ]]
|
40
addons/mod_tool/interface/global/resizeable_text_edit.gd
Normal file
40
addons/mod_tool/interface/global/resizeable_text_edit.gd
Normal file
@@ -0,0 +1,40 @@
|
||||
@tool
|
||||
extends VSplitContainer
|
||||
|
||||
var previous_size := 0
|
||||
var is_pressed := false
|
||||
@onready var text_edit := get_child(0) as TextEdit
|
||||
@onready var min_size_y := text_edit.custom_minimum_size.y as int
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
connect("dragged", Callable(self, "_on_dragged"))
|
||||
connect("gui_input", Callable(self, "_on_gui_input"))
|
||||
if get_child_count() < 2:
|
||||
add_child(Control.new())
|
||||
|
||||
|
||||
func _on_dragged(offset: int) -> void:
|
||||
# offset is cumulative for the whole drag move
|
||||
var new_size := previous_size + offset
|
||||
|
||||
if new_size < min_size_y:
|
||||
text_edit.custom_minimum_size.y = min_size_y
|
||||
else:
|
||||
text_edit.custom_minimum_size.y = new_size
|
||||
|
||||
|
||||
func _on_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if event.pressed and not is_pressed:
|
||||
is_pressed = true
|
||||
previous_size = text_edit.custom_minimum_size.y
|
||||
else:
|
||||
is_pressed = false
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
if not get_child(0) is TextEdit:
|
||||
return ["First child needs to be a TextEdit"]
|
||||
return [""]
|
@@ -0,0 +1 @@
|
||||
uid://d0mkr0c6ga1va
|
63
addons/mod_tool/interface/hook_gen/hook_gen.gd
Normal file
63
addons/mod_tool/interface/hook_gen/hook_gen.gd
Normal file
@@ -0,0 +1,63 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceHookGen
|
||||
extends Window
|
||||
|
||||
|
||||
signal hooks_exist_pressed
|
||||
|
||||
@onready var mod_tool_store: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
@onready var info_output: RichTextLabel = %InfoOutput
|
||||
@onready var restart: Window = %Restart
|
||||
@onready var button_gen_start: Button = %ButtonGenStart
|
||||
|
||||
|
||||
func generate_hooks() -> void:
|
||||
# Get all script not in addons or mods-unpacked
|
||||
var all_script_file_paths := ModToolUtils.get_flat_view_dict("res://", "", [&"gd"], false, false, [&"addons", &"mods-unpacked"])
|
||||
|
||||
for script_file_path in all_script_file_paths:
|
||||
if mod_tool_store.hooked_scripts.has(script_file_path):
|
||||
info_output.add_text("Skipping - Hooks already exists for \"%s\" \n" % script_file_path)
|
||||
continue
|
||||
|
||||
var error := ModToolHookGen.transform_one(script_file_path, mod_tool_store)
|
||||
|
||||
if not error == OK:
|
||||
info_output.add_text("ERROR: Accessing file at path \"%s\" failed with error: %s \n" % [script_file_path, error_string(error)])
|
||||
else:
|
||||
info_output.add_text("Added Hooks for \"%s\" \n" % script_file_path)
|
||||
|
||||
mod_tool_store.is_hook_generation_done = true
|
||||
info_output.add_text("Mod Hook generation completed successfully!\n")
|
||||
|
||||
mod_tool_store.save_store()
|
||||
restart.show()
|
||||
|
||||
|
||||
func _on_button_pressed() -> void:
|
||||
button_gen_start.disabled = true
|
||||
generate_hooks()
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
hide()
|
||||
|
||||
|
||||
func _on_button_restart_now_pressed() -> void:
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
EditorInterface.restart_editor()
|
||||
|
||||
|
||||
func _on_button_restart_later_pressed() -> void:
|
||||
restart.hide()
|
||||
hide()
|
||||
|
||||
|
||||
func _on_restart_close_requested() -> void:
|
||||
restart.hide()
|
||||
|
||||
|
||||
func _on_button_hooks_exist_pressed() -> void:
|
||||
mod_tool_store.is_hook_generation_done = true
|
||||
hooks_exist_pressed.emit()
|
||||
hide()
|
1
addons/mod_tool/interface/hook_gen/hook_gen.gd.uid
Normal file
1
addons/mod_tool/interface/hook_gen/hook_gen.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://j06uud1328hl
|
105
addons/mod_tool/interface/hook_gen/hook_gen.tscn
Normal file
105
addons/mod_tool/interface/hook_gen/hook_gen.tscn
Normal file
@@ -0,0 +1,105 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://cpll5clcnemyj"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/hook_gen/hook_gen.gd" id="1_lrahv"]
|
||||
|
||||
[node name="HookGen" type="Window"]
|
||||
title = "Mod Dev Tool"
|
||||
initial_position = 1
|
||||
size = Vector2i(640, 375)
|
||||
wrap_controls = true
|
||||
script = ExtResource("1_lrahv")
|
||||
|
||||
[node name="Restart" type="Window" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Mod Dev Tool"
|
||||
initial_position = 1
|
||||
size = Vector2i(440, 117)
|
||||
visible = false
|
||||
wrap_controls = true
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Restart"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="Restart/MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LabelInfoText" type="RichTextLabel" parent="Restart/MarginContainer/VBC"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
text = "Successfully generated mod hooks.
|
||||
To start modding, a restart of the editor is required."
|
||||
fit_content = true
|
||||
|
||||
[node name="HBC" type="HBoxContainer" parent="Restart/MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_constants/separation = 30
|
||||
|
||||
[node name="ButtonRestartNow" type="Button" parent="Restart/MarginContainer/VBC/HBC"]
|
||||
layout_mode = 2
|
||||
text = "Restart Now"
|
||||
|
||||
[node name="ButtonRestartLater" type="Button" parent="Restart/MarginContainer/VBC/HBC"]
|
||||
layout_mode = 2
|
||||
text = "Restart Later"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 15
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LabelInfoText" type="RichTextLabel" parent="MarginContainer/VBC"]
|
||||
custom_minimum_size = Vector2(600, 0)
|
||||
layout_mode = 2
|
||||
text = "This will modify all existing scripts in the project, so please make sure to save your files before continuing.
|
||||
|
||||
The process may take some time depending on the number of scripts."
|
||||
fit_content = true
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ButtonGenStart" type="Button" parent="MarginContainer/VBC/VBC"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Generate mod hooks"
|
||||
|
||||
[node name="ButtonHooksExist" type="Button" parent="MarginContainer/VBC/VBC"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Hooks already exist"
|
||||
|
||||
[node name="VBC2" type="VBoxContainer" parent="MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 15
|
||||
|
||||
[node name="InfoOutput" type="RichTextLabel" parent="MarginContainer/VBC/VBC2"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(600, 100)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
scroll_following = true
|
||||
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
||||
[connection signal="close_requested" from="Restart" to="." method="_on_restart_close_requested"]
|
||||
[connection signal="pressed" from="Restart/MarginContainer/VBC/HBC/ButtonRestartNow" to="." method="_on_button_restart_now_pressed"]
|
||||
[connection signal="pressed" from="Restart/MarginContainer/VBC/HBC/ButtonRestartLater" to="." method="_on_button_restart_later_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBC/VBC/ButtonGenStart" to="." method="_on_button_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBC/VBC/ButtonHooksExist" to="." method="_on_button_hooks_exist_pressed"]
|
59
addons/mod_tool/interface/hook_restore/hook_restore.gd
Normal file
59
addons/mod_tool/interface/hook_restore/hook_restore.gd
Normal file
@@ -0,0 +1,59 @@
|
||||
@tool
|
||||
class_name ModToolInterfaceHookRestore
|
||||
extends Window
|
||||
|
||||
|
||||
@onready var mod_tool_store: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
@onready var info_output: RichTextLabel = %InfoOutput
|
||||
@onready var restart: Window = %Restart
|
||||
@onready var button_restore_start: Button = %ButtonRestoreStart
|
||||
|
||||
|
||||
func start_restore() -> void:
|
||||
# Get all script not in addons or mods-unpacked
|
||||
var all_script_file_paths := ModToolUtils.get_flat_view_dict("res://", "", [&"gd"], false, false, [&"addons", &"mods-unpacked"])
|
||||
|
||||
var encountered_error := false
|
||||
|
||||
for script_path in mod_tool_store.hooked_scripts.keys():
|
||||
var error := ModToolHookGen.restore(script_path, mod_tool_store)
|
||||
|
||||
if not error == OK:
|
||||
encountered_error = true
|
||||
info_output.add_text("ERROR: Accessing file at path \"%s\" failed with error: %s \n" % [script_path, error_string(error)])
|
||||
break
|
||||
|
||||
info_output.add_text("Restored \"%s\" \n" % script_path)
|
||||
|
||||
if encountered_error:
|
||||
info_output.add_text("ERROR: Restore aborted.\n")
|
||||
else:
|
||||
mod_tool_store.is_hook_generation_done = false
|
||||
info_output.add_text("Restore completed successfully!\n")
|
||||
|
||||
mod_tool_store.save_store()
|
||||
|
||||
restart.show()
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
hide()
|
||||
|
||||
|
||||
func _on_button_restart_now_pressed() -> void:
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
EditorInterface.restart_editor()
|
||||
|
||||
|
||||
func _on_button_restart_later_pressed() -> void:
|
||||
restart.hide()
|
||||
hide()
|
||||
|
||||
|
||||
func _on_restart_close_requested() -> void:
|
||||
restart.hide()
|
||||
|
||||
|
||||
func _on_button_restore_start_pressed() -> void:
|
||||
button_restore_start.disabled = true
|
||||
start_restore()
|
@@ -0,0 +1 @@
|
||||
uid://dy72tnwnjxxpb
|
100
addons/mod_tool/interface/hook_restore/hook_restore.tscn
Normal file
100
addons/mod_tool/interface/hook_restore/hook_restore.tscn
Normal file
@@ -0,0 +1,100 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://camcc83bvu086"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/hook_restore/hook_restore.gd" id="1_wq3ld"]
|
||||
|
||||
[node name="HookRestore" type="Window"]
|
||||
title = "Mod Dev Tool"
|
||||
initial_position = 1
|
||||
size = Vector2i(640, 375)
|
||||
wrap_controls = true
|
||||
script = ExtResource("1_wq3ld")
|
||||
|
||||
[node name="Restart" type="Window" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Mod Dev Tool"
|
||||
initial_position = 1
|
||||
size = Vector2i(440, 117)
|
||||
visible = false
|
||||
wrap_controls = true
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Restart"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="Restart/MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
alignment = 1
|
||||
|
||||
[node name="LabelInfoText" type="RichTextLabel" parent="Restart/MarginContainer/VBC"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
text = "Successfully restored all scripts.
|
||||
A restart of the editor is required."
|
||||
fit_content = true
|
||||
|
||||
[node name="HBC" type="HBoxContainer" parent="Restart/MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_constants/separation = 30
|
||||
|
||||
[node name="ButtonRestartNow" type="Button" parent="Restart/MarginContainer/VBC/HBC"]
|
||||
layout_mode = 2
|
||||
text = "Restart Now"
|
||||
|
||||
[node name="ButtonRestartLater" type="Button" parent="Restart/MarginContainer/VBC/HBC"]
|
||||
layout_mode = 2
|
||||
text = "Restart Later"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 15
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LabelInfoText" type="RichTextLabel" parent="MarginContainer/VBC"]
|
||||
custom_minimum_size = Vector2(600, 0)
|
||||
layout_mode = 2
|
||||
text = "This will restore all scripts to their state before mod hooks were added. Any changes made to these scripts will be lost!
|
||||
|
||||
The process may take some time depending on the number of scripts."
|
||||
fit_content = true
|
||||
|
||||
[node name="VBC" type="VBoxContainer" parent="MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ButtonRestoreStart" type="Button" parent="MarginContainer/VBC/VBC"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Start Restore"
|
||||
|
||||
[node name="VBC2" type="VBoxContainer" parent="MarginContainer/VBC"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 15
|
||||
|
||||
[node name="InfoOutput" type="RichTextLabel" parent="MarginContainer/VBC/VBC2"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(600, 100)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
scroll_following = true
|
||||
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
||||
[connection signal="close_requested" from="Restart" to="." method="_on_restart_close_requested"]
|
||||
[connection signal="pressed" from="Restart/MarginContainer/VBC/HBC/ButtonRestartNow" to="." method="_on_button_restart_now_pressed"]
|
||||
[connection signal="pressed" from="Restart/MarginContainer/VBC/HBC/ButtonRestartLater" to="." method="_on_button_restart_later_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBC/VBC/ButtonRestoreStart" to="." method="_on_button_restore_start_pressed"]
|
278
addons/mod_tool/interface/manifest_editor/manifest_editor.gd
Normal file
278
addons/mod_tool/interface/manifest_editor/manifest_editor.gd
Normal file
@@ -0,0 +1,278 @@
|
||||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
|
||||
var input_fields := []
|
||||
|
||||
@onready var mod_tool_store: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
@onready var manifest_input_vbox := $"%InputVBox"
|
||||
@onready var input_incompatibilities: ModToolInterfaceInputString = $"%Incompatibilities"
|
||||
@onready var input_dependencies: ModToolInterfaceInputString = $"%Dependencies"
|
||||
@onready var input_optional_dependencies: ModToolInterfaceInputString = $"%OptionalDependencies"
|
||||
@onready var input_load_before: ModToolInterfaceInputString = $"%LoadBefore"
|
||||
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$VBox/Panel.add_theme_stylebox_override("panel", ThemeDB.get_default_theme().get_stylebox("bg", "ItemList"))
|
||||
# Setup input fields
|
||||
for node in manifest_input_vbox.get_children():
|
||||
if node is ModToolInterfaceInputString:
|
||||
input_fields.append(node)
|
||||
|
||||
|
||||
func load_manifest() -> void:
|
||||
var manifest_dict_json := _ModLoaderFile.get_json_as_dict(mod_tool_store.path_manifest)
|
||||
mod_tool_store.manifest_data = ModManifest.new(manifest_dict_json, mod_tool_store.path_mod_dir)
|
||||
ModToolUtils.output_info("Loaded manifest from " + mod_tool_store.path_manifest)
|
||||
|
||||
|
||||
func save_manifest() -> void:
|
||||
var invalid_inputs := get_invalid()
|
||||
|
||||
if invalid_inputs.size() == 0:
|
||||
var _is_success := ModToolUtils.save_to_manifest_json(mod_tool_store.manifest_data, mod_tool_store.path_manifest)
|
||||
else:
|
||||
ModToolUtils.output_error('Invalid Manifest - Manifest not saved! Please check your inputs in the following fields -> ' + ", ".join(invalid_inputs))
|
||||
|
||||
|
||||
func update_ui() -> void:
|
||||
# For each input field
|
||||
for input in input_fields:
|
||||
# Check if the key used in the ModToolInterfaceInputString instance is in the data_dict.
|
||||
if mod_tool_store.manifest_data.get(input.key):
|
||||
var value = mod_tool_store.manifest_data.get(input.key)
|
||||
|
||||
# If the value is an Array create a comma separated list
|
||||
if value is PackedStringArray:
|
||||
input.input_text = ", ".join(value)
|
||||
# Else convert the value to a string
|
||||
else:
|
||||
input.input_text = str(value)
|
||||
# If the key is not in the data clear the input
|
||||
else:
|
||||
input.input_text = ""
|
||||
|
||||
|
||||
# Returns an array of invalid fields
|
||||
func get_invalid() -> Array:
|
||||
var invalid_fields := []
|
||||
|
||||
for input in input_fields:
|
||||
if not input.is_valid:
|
||||
invalid_fields.append(input.label_text)
|
||||
|
||||
return invalid_fields
|
||||
|
||||
|
||||
func _update_manifest_value(input: ModToolInterfaceInputString, new_value) -> void:
|
||||
if mod_tool_store.manifest_data:
|
||||
mod_tool_store.manifest_data.set(input.key, new_value)
|
||||
|
||||
|
||||
func _on_SaveManifest_pressed() -> void:
|
||||
save_manifest()
|
||||
|
||||
|
||||
# Validated StringInputs
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func _on_ModName_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
_update_manifest_value(input_node, new_text)
|
||||
input_node.validate(mod_tool_store.manifest_data.is_name_or_namespace_valid(new_text, true))
|
||||
|
||||
|
||||
func _on_Namespace_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
_update_manifest_value(input_node, new_text)
|
||||
input_node.validate(mod_tool_store.manifest_data.is_name_or_namespace_valid(new_text, true))
|
||||
|
||||
|
||||
func _on_Version_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
_update_manifest_value(input_node, new_text)
|
||||
input_node.validate(mod_tool_store.manifest_data.is_semver_valid("", new_text, "version", true))
|
||||
|
||||
|
||||
# When dealing with Inputs that depend on other Inputs, the `input_node` is not utilized.
|
||||
# This is because the `value_changed` signal is connected to this method for all relevant inputs.
|
||||
# As a result, the input_node would retrieve multiple different nodes, which should not be updated but rather revalidated.
|
||||
# In such cases, the input node is directly referenced to prevent overwriting the values in other input fields.
|
||||
func _on_Dependencies_value_changed(new_text: String, input_node: ModToolInterfaceInputString, validate_only: bool) -> void:
|
||||
var dependencies: PackedStringArray
|
||||
|
||||
if validate_only:
|
||||
dependencies = mod_tool_store.manifest_data.dependencies
|
||||
else:
|
||||
dependencies = input_dependencies.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_dependencies, dependencies)
|
||||
|
||||
var is_id_array_valid := mod_tool_store.manifest_data.is_mod_id_array_valid(mod_tool_store.name_mod_dir, dependencies, "dependencies", true)
|
||||
var is_distinct_mod_id_incompatibilities := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
dependencies,
|
||||
mod_tool_store.manifest_data.incompatibilities,
|
||||
["dependencies", "incompatibilities"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
var is_distinct_mod_id_optional_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
dependencies,
|
||||
mod_tool_store.manifest_data.optional_dependencies,
|
||||
["dependencies", "optional_dependencies"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
|
||||
input_dependencies.validate(
|
||||
is_id_array_valid and
|
||||
is_distinct_mod_id_incompatibilities and
|
||||
is_distinct_mod_id_optional_dependencies
|
||||
)
|
||||
|
||||
|
||||
# When dealing with Inputs that depend on other Inputs, the `input_node` is not utilized.
|
||||
# This is because the `value_changed` signal is connected to this method for all relevant inputs.
|
||||
# As a result, the input_node would retrieve multiple different nodes, which should not be updated but rather revalidated.
|
||||
# In such cases, the input node is directly referenced to prevent overwriting the values in other input fields.
|
||||
func _on_OptionalDependencies_value_changed(new_text: String, input_node: ModToolInterfaceInputString, validate_only: bool) -> void:
|
||||
var optional_dependencies: PackedStringArray
|
||||
|
||||
if validate_only:
|
||||
optional_dependencies = mod_tool_store.manifest_data.optional_dependencies
|
||||
else:
|
||||
optional_dependencies = input_optional_dependencies.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_optional_dependencies, optional_dependencies)
|
||||
|
||||
var is_id_array_valid := mod_tool_store.manifest_data.is_mod_id_array_valid(mod_tool_store.name_mod_dir, optional_dependencies, "optional_dependencies", true)
|
||||
var is_distinct_mod_id_incompatibilities := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
optional_dependencies,
|
||||
mod_tool_store.manifest_data.incompatibilities,
|
||||
["optional_dependencies", "incompatibilities"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
var is_distinct_mod_id_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
optional_dependencies,
|
||||
mod_tool_store.manifest_data.dependencies,
|
||||
["optional_dependencies", "dependencies"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
|
||||
input_optional_dependencies.validate(
|
||||
is_id_array_valid and
|
||||
is_distinct_mod_id_incompatibilities and
|
||||
is_distinct_mod_id_dependencies
|
||||
)
|
||||
|
||||
|
||||
func _on_CompatibleModLoaderVersions_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
var compatible_modloader_versions := input_node.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_node, compatible_modloader_versions)
|
||||
input_node.validate(mod_tool_store.manifest_data.is_semver_version_array_valid(mod_tool_store.name_mod_dir, compatible_modloader_versions, "Compatible ModLoader Versions", true))
|
||||
|
||||
|
||||
# When dealing with Inputs that depend on other Inputs, the `input_node` is not utilized.
|
||||
# This is because the `value_changed` signal is connected to this method for all relevant inputs.
|
||||
# As a result, the input_node would retrieve multiple different nodes, which should not be updated but rather revalidated.
|
||||
# In such cases, the input node is directly referenced to prevent overwriting the values in other input fields.
|
||||
func _on_Incompatibilities_value_changed(new_text: String, input_node: ModToolInterfaceInputString, validate_only: bool) -> void:
|
||||
var incompatibilities: PackedStringArray
|
||||
|
||||
if validate_only:
|
||||
incompatibilities = mod_tool_store.manifest_data.incompatibilities
|
||||
else:
|
||||
incompatibilities = input_incompatibilities.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_incompatibilities, incompatibilities)
|
||||
|
||||
var is_mod_id_array_valid := mod_tool_store.manifest_data.is_mod_id_array_valid(mod_tool_store.name_mod_dir, incompatibilities, "incompatibilities", true)
|
||||
var is_distinct_mod_id_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
mod_tool_store.manifest_data.dependencies,
|
||||
incompatibilities,
|
||||
["dependencies", "incompatibilities"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
var is_distinct_mod_id_optional_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
mod_tool_store.manifest_data.optional_dependencies,
|
||||
incompatibilities,
|
||||
["optional_dependencies", "incompatibilities"],
|
||||
"",
|
||||
true
|
||||
)
|
||||
|
||||
input_incompatibilities.validate(
|
||||
is_mod_id_array_valid and
|
||||
is_distinct_mod_id_dependencies and
|
||||
is_distinct_mod_id_optional_dependencies
|
||||
)
|
||||
|
||||
|
||||
# When dealing with Inputs that depend on other Inputs, the `input_node` is not utilized.
|
||||
# This is because the `value_changed` signal is connected to this method for all relevant inputs.
|
||||
# As a result, the input_node would retrieve multiple different nodes, which should not be updated but rather revalidated.
|
||||
# In such cases, the input node is directly referenced to prevent overwriting the values in other input fields.
|
||||
func _on_LoadBefore_value_changed(new_text: String, input_node: ModToolInterfaceInputString, validate_only: bool) -> void:
|
||||
var load_before: PackedStringArray
|
||||
|
||||
if validate_only:
|
||||
load_before = mod_tool_store.manifest_data.load_before
|
||||
else:
|
||||
load_before = input_load_before.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_load_before, load_before)
|
||||
|
||||
var is_mod_id_array_valid := mod_tool_store.manifest_data.is_mod_id_array_valid(mod_tool_store.name_mod_dir, load_before, "load_before", true)
|
||||
var is_distinct_mod_id_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
load_before,
|
||||
mod_tool_store.manifest_data.dependencies,
|
||||
["load_before", "dependencies"],
|
||||
"\"load_before\" should be handled as optional dependency adding it to \"dependencies\" will cancel out the desired effect.",
|
||||
true
|
||||
)
|
||||
var is_distinct_mod_id_optional_dependencies := mod_tool_store.manifest_data.validate_distinct_mod_ids_in_arrays(
|
||||
mod_tool_store.name_mod_dir,
|
||||
load_before,
|
||||
mod_tool_store.manifest_data.optional_dependencies,
|
||||
["load_before", "optional_dependencies"],
|
||||
"\"load_before\" can be viewed as optional dependency, please remove the duplicate mod-id.",
|
||||
true
|
||||
)
|
||||
|
||||
input_load_before.validate(
|
||||
is_mod_id_array_valid and
|
||||
is_distinct_mod_id_dependencies and
|
||||
is_distinct_mod_id_optional_dependencies
|
||||
)
|
||||
|
||||
|
||||
# Non Validated StringInputs
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func _on_WebsiteUrl_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
_update_manifest_value(input_node, new_text)
|
||||
|
||||
|
||||
func _on_Description_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
_update_manifest_value(input_node, new_text)
|
||||
|
||||
|
||||
func _on_Authors_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
var authors := input_node.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_node, authors)
|
||||
|
||||
|
||||
func _on_CompatibleGameVersions_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
var compatible_game_versions := input_node.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_node, compatible_game_versions)
|
||||
|
||||
|
||||
func _on_Tags_value_changed(new_text: String, input_node: ModToolInterfaceInputString) -> void:
|
||||
var tags := input_node.get_input_as_array_from_comma_separated_string()
|
||||
_update_manifest_value(input_node, tags)
|
@@ -0,0 +1 @@
|
||||
uid://c4jhb2cmcvg3k
|
289
addons/mod_tool/interface/manifest_editor/manifest_editor.tscn
Normal file
289
addons/mod_tool/interface/manifest_editor/manifest_editor.tscn
Normal file
@@ -0,0 +1,289 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://hpefgw6k5qpq"]
|
||||
|
||||
[ext_resource type="PackedScene" path="res://addons/mod_tool/interface/global/input_string_multiline.tscn" id="1"]
|
||||
[ext_resource type="PackedScene" path="res://addons/mod_tool/interface/global/input_string.tscn" id="2"]
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/manifest_editor/manifest_editor.gd" id="4"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pun0q"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(1, 0.365, 0.365, 1)
|
||||
draw_center = false
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
corner_detail = 1
|
||||
|
||||
[node name="ModManifest" type="PanelContainer"]
|
||||
unique_name_in_owner = true
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("4")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBox2" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBox/HBox2"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Metadata required for your mod"
|
||||
|
||||
[node name="ErrorLabel" type="Label" parent="VBox/HBox2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Manifest is valid"
|
||||
|
||||
[node name="ShouldValidate" type="CheckButton" parent="VBox/HBox2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBox/HBox2"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SaveManifest" type="Button" parent="VBox/HBox2"]
|
||||
layout_mode = 2
|
||||
text = "Save to manifest.json"
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_pun0q")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBox/Panel"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
follow_focus = true
|
||||
|
||||
[node name="InputVBox" type="VBoxContainer" parent="VBox/Panel/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Category" type="LineEdit" parent="VBox/Panel/ScrollContainer/InputVBox"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
mouse_default_cursor_shape = 0
|
||||
text = "Manifest"
|
||||
editable = false
|
||||
context_menu_enabled = false
|
||||
virtual_keyboard_enabled = false
|
||||
shortcut_keys_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
|
||||
[node name="ModName" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Name of the Mod.
|
||||
Only letters, numbers and underscores allowed."
|
||||
mouse_default_cursor_shape = 16
|
||||
is_required = true
|
||||
key = "name"
|
||||
label_text = "Mod Name"
|
||||
hint_text = "Name of the Mod.
|
||||
Only letters, numbers and underscores allowed."
|
||||
|
||||
[node name="Namespace" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Namespace of the Mod.
|
||||
Often just the main author or team name.
|
||||
Only letters, numbers and underscores allowed."
|
||||
mouse_default_cursor_shape = 16
|
||||
is_required = true
|
||||
key = "mod_namespace"
|
||||
label_text = "Namespace"
|
||||
hint_text = "Namespace of the Mod.
|
||||
Often just the main author or team name.
|
||||
Only letters, numbers and underscores allowed."
|
||||
|
||||
[node name="Version" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Semantic version string.
|
||||
Only integers and periods allowed.
|
||||
Format: {major}.{minor}.{patch}
|
||||
For reference, see https://semver.org"
|
||||
mouse_default_cursor_shape = 16
|
||||
input_text = "0.0.1"
|
||||
is_required = true
|
||||
key = "version_number"
|
||||
label_text = "Version"
|
||||
hint_text = "Semantic version string.
|
||||
Only integers and periods allowed.
|
||||
Format: {major}.{minor}.{patch}
|
||||
For reference, see https://semver.org"
|
||||
|
||||
[node name="WebsiteUrl" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "URL for your website or repository."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "https://example.com"
|
||||
key = "website_url"
|
||||
label_text = "Mod website URL"
|
||||
hint_text = "URL for your website or repository."
|
||||
|
||||
[node name="Dependencies" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Dependencies can't be in Incompatibilities or Optional Dependencies.
|
||||
"
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Namespace-ModName, Author-Name"
|
||||
key = "dependencies"
|
||||
label_text = "Dependencies"
|
||||
hint_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Dependencies can't be in Incompatibilities or Optional Dependencies.
|
||||
"
|
||||
|
||||
[node name="Description" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("1")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
key = "description"
|
||||
label_text = "Description"
|
||||
|
||||
[node name="Category2" type="LineEdit" parent="VBox/Panel/ScrollContainer/InputVBox"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
mouse_default_cursor_shape = 0
|
||||
text = "Manifest Extra"
|
||||
editable = false
|
||||
context_menu_enabled = false
|
||||
virtual_keyboard_enabled = false
|
||||
shortcut_keys_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
|
||||
[node name="Authors" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of Authors"
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Author1, Autor2"
|
||||
key = "authors"
|
||||
label_text = "Authors"
|
||||
hint_text = "Comma-separated list of Authors"
|
||||
|
||||
[node name="Incompatibilities" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Incompatible Mods can't be in dependencies or optional dependencies."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Namespace-ModName, Author-Name"
|
||||
key = "incompatibilities"
|
||||
label_text = "Incompatible Mods"
|
||||
hint_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Incompatible Mods can't be in dependencies or optional dependencies."
|
||||
|
||||
[node name="OptionalDependencies" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Optional Dependencies can't be in Incompatibilities or Dependencies."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Namespace-ModName, Author-Name"
|
||||
key = "optional_dependencies"
|
||||
label_text = "Optional Dependencies"
|
||||
hint_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Optional Dependencies can't be in Incompatibilities or Dependencies."
|
||||
|
||||
[node name="LoadBefore" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Should be handled as optional dependency adding it to \"dependencies\" will cancel out the desired effect.
|
||||
Can be viewed as optional dependency, please remove duplicate mod-id from \"optional_dependencies\"."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Namespace-ModName, Author-Name"
|
||||
key = "load_before"
|
||||
label_text = "Load Before"
|
||||
hint_text = "Comma-separated list of mod IDs.
|
||||
Only letters, numbers and underscores allowed.
|
||||
A single dash in the middle is required.
|
||||
Format: {namespace}-{name}
|
||||
Should be handled as optional dependency adding it to \"dependencies\" will cancel out the desired effect.
|
||||
Can be viewed as optional dependency, please remove duplicate mod-id from \"optional_dependencies\"."
|
||||
|
||||
[node name="CompatibleGameVersions" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of valid game versions."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "1.0.0, 1.2.0"
|
||||
key = "compatible_game_version"
|
||||
label_text = "Compatible Game Versions"
|
||||
hint_text = "Comma-separated list of valid game versions."
|
||||
|
||||
[node name="CompatibleModLoaderVersions" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of ModLoader versions."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "5.0.0, 4.1.0"
|
||||
is_required = true
|
||||
key = "compatible_mod_loader_version"
|
||||
label_text = "Compatible Mod Loader Versions"
|
||||
hint_text = "Comma-separated list of ModLoader versions."
|
||||
|
||||
[node name="Tags" parent="VBox/Panel/ScrollContainer/InputVBox" instance=ExtResource("2")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Comma-separated list of tags that describe your mod.."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_placeholder = "Tag1, Tag2"
|
||||
key = "tags"
|
||||
label_text = "Tags"
|
||||
hint_text = "Comma-separated list of tags that describe your mod.."
|
||||
|
||||
[connection signal="pressed" from="VBox/HBox2/SaveManifest" to="." method="_on_SaveManifest_pressed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/ModName" to="." method="_on_ModName_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Namespace" to="." method="_on_Namespace_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Version" to="." method="_on_Version_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/WebsiteUrl" to="." method="_on_WebsiteUrl_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Dependencies" to="." method="_on_OptionalDependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Dependencies" to="." method="_on_Incompatibilities_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Dependencies" to="." method="_on_Dependencies_value_changed" binds= [false]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Description" to="." method="_on_Description_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Authors" to="." method="_on_Authors_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Incompatibilities" to="." method="_on_OptionalDependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Incompatibilities" to="." method="_on_Incompatibilities_value_changed" binds= [false]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Incompatibilities" to="." method="_on_Dependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/OptionalDependencies" to="." method="_on_OptionalDependencies_value_changed" binds= [false]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/OptionalDependencies" to="." method="_on_Incompatibilities_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/OptionalDependencies" to="." method="_on_Dependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/LoadBefore" to="." method="_on_OptionalDependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/LoadBefore" to="." method="_on_LoadBefore_value_changed" binds= [false]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/LoadBefore" to="." method="_on_Dependencies_value_changed" flags=3 binds= [true]]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/CompatibleGameVersions" to="." method="_on_CompatibleGameVersions_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/CompatibleModLoaderVersions" to="." method="_on_CompatibleModLoaderVersions_value_changed"]
|
||||
[connection signal="value_changed" from="VBox/Panel/ScrollContainer/InputVBox/Tags" to="." method="_on_Tags_value_changed"]
|
210
addons/mod_tool/interface/panel/tools_panel.gd
Normal file
210
addons/mod_tool/interface/panel/tools_panel.gd
Normal file
@@ -0,0 +1,210 @@
|
||||
@tool
|
||||
class_name ModToolsPanel
|
||||
extends Control
|
||||
|
||||
|
||||
# passed from the EditorPlugin
|
||||
var mod_tool_store: ModToolStore
|
||||
var editor_plugin: EditorPlugin: set = set_editor_plugin
|
||||
var context_actions: FileSystemContextActions
|
||||
|
||||
var tab_parent_bottom_panel: PanelContainer
|
||||
var log_richtext_label: RichTextLabel
|
||||
var log_dock_button: Button
|
||||
|
||||
@onready var mod_tool_store_node: ModToolStore = get_node_or_null("/root/ModToolStore")
|
||||
@onready var tab_container := $"%TabContainer"
|
||||
@onready var create_mod := $"%CreateMod"
|
||||
@onready var select_mod := $"%SelectMod"
|
||||
@onready var label_output := $"%Output"
|
||||
@onready var mod_id := $"%ModId"
|
||||
@onready var manifest_editor := $"%Manifest Editor"
|
||||
@onready var export_path := $"%ExportPath"
|
||||
@onready var file_dialog := $"%FileDialog"
|
||||
@onready var hook_gen: ModToolInterfaceHookGen = %HookGen
|
||||
@onready var hook_restore: ModToolInterfaceHookRestore = %HookRestore
|
||||
@onready var button_add_hooks: Button = %AddHooks
|
||||
@onready var button_restore: Button = %Restore
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
tab_parent_bottom_panel = get_parent().get_parent() as PanelContainer
|
||||
|
||||
get_log_nodes()
|
||||
|
||||
if mod_tool_store:
|
||||
if mod_tool_store.is_hook_generation_done:
|
||||
button_add_hooks.hide()
|
||||
else:
|
||||
button_restore.hide()
|
||||
# Load manifest.json file
|
||||
if _ModLoaderFile.file_exists(mod_tool_store.path_manifest):
|
||||
manifest_editor.load_manifest()
|
||||
manifest_editor.update_ui()
|
||||
else:
|
||||
# Load template Manifest
|
||||
var template_manifest_data := _ModLoaderFile.get_json_as_dict("res://addons/mod_tool/templates/minimal/manifest.json")
|
||||
mod_tool_store.manifest_data = ModManifest.new(template_manifest_data, "")
|
||||
|
||||
_update_ui()
|
||||
|
||||
|
||||
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
||||
editor_plugin = plugin
|
||||
|
||||
mod_tool_store.editor_plugin = editor_plugin
|
||||
mod_tool_store.editor_file_system = EditorInterface.get_resource_filesystem()
|
||||
mod_tool_store.editor_base_control = EditorInterface.get_base_control()
|
||||
|
||||
context_actions = FileSystemContextActions.new(
|
||||
mod_tool_store,
|
||||
EditorInterface.get_file_system_dock()
|
||||
)
|
||||
|
||||
|
||||
func get_log_nodes() -> void:
|
||||
var editor_log := get_parent().get_child(0)
|
||||
log_richtext_label = editor_log.get_child(1) as RichTextLabel
|
||||
if not log_richtext_label:
|
||||
# on project load it can happen that these nodes don't exist yet, wait for parent
|
||||
await get_parent().ready
|
||||
log_richtext_label = editor_log.get_child(1) as RichTextLabel
|
||||
|
||||
# The button hbox should be last, but here it is second from last for some reason
|
||||
var dock_tool_button_bar: HBoxContainer = get_parent().get_child(get_parent().get_child_count() -2)
|
||||
log_dock_button = dock_tool_button_bar.get_child(0).get_child(0)
|
||||
|
||||
|
||||
# Removes the last error line from the output console as if nothing happened
|
||||
# used in the json validation since the error is displayed right there and
|
||||
# it causes a lot of clutter otherwise
|
||||
func discard_last_console_error() -> void:
|
||||
# If the console is flooded anyway, ignore
|
||||
var line_count := log_richtext_label.get_line_count()
|
||||
if line_count > 1000:
|
||||
return
|
||||
|
||||
# The last line is an empty line, remove the one before that
|
||||
log_richtext_label.remove_line(line_count -2)
|
||||
log_richtext_label.add_text("\n")
|
||||
|
||||
# If there is an error in the console already, leave the circle on the tool button
|
||||
# All error lines have a space in the beginnig to separate from the circle image
|
||||
# Not the safest way to check, but it's the only one it seems
|
||||
for line in log_richtext_label.text.split("\n"):
|
||||
if (line as String).begins_with(" "):
|
||||
return
|
||||
|
||||
# If there were no other error lines, remove the icon
|
||||
# Setting to null will crash the editor occasionally, this does not
|
||||
if log_dock_button:
|
||||
log_dock_button.icon = CompressedTexture2D.new()
|
||||
|
||||
|
||||
func show_manifest_editor() -> void:
|
||||
tab_container.current_tab = 0
|
||||
|
||||
|
||||
func show_config_editor() -> void:
|
||||
tab_container.current_tab = 1
|
||||
|
||||
|
||||
func _update_ui() -> void:
|
||||
if not mod_tool_store:
|
||||
return
|
||||
mod_id.input_text = mod_tool_store.name_mod_dir
|
||||
export_path.input_text = mod_tool_store.path_export_dir
|
||||
|
||||
|
||||
func _is_mod_dir_valid() -> bool:
|
||||
# Check if Mod ID is given
|
||||
if mod_tool_store.name_mod_dir == '':
|
||||
ModToolUtils.output_error("Please provide a Mod ID")
|
||||
return false
|
||||
|
||||
# Check if mod dir exists
|
||||
if not _ModLoaderFile.dir_exists(mod_tool_store.path_mod_dir):
|
||||
ModToolUtils.output_error("Mod folder %s does not exist" % mod_tool_store.path_mod_dir)
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func load_mod(name_mod_dir: String) -> void:
|
||||
# Set the dir name
|
||||
mod_tool_store.name_mod_dir = name_mod_dir
|
||||
|
||||
# Load Manifest
|
||||
manifest_editor.load_manifest()
|
||||
manifest_editor.update_ui()
|
||||
|
||||
# TODO: Load Mod Config if existing
|
||||
|
||||
ModToolUtils.output_info("Mod \"%s\" loaded." % name_mod_dir)
|
||||
|
||||
|
||||
func _on_export_pressed() -> void:
|
||||
if _is_mod_dir_valid():
|
||||
var zipper := ModToolZipBuilder.new()
|
||||
zipper.build_zip(mod_tool_store)
|
||||
|
||||
|
||||
func _on_clear_output_pressed() -> void:
|
||||
label_output.clear()
|
||||
|
||||
|
||||
func _on_copy_output_pressed() -> void:
|
||||
DisplayServer.clipboard_set(label_output.text)
|
||||
|
||||
|
||||
func _on_save_manifest_pressed() -> void:
|
||||
manifest_editor.save_manifest()
|
||||
|
||||
|
||||
func _on_export_settings_create_new_mod_pressed() -> void:
|
||||
create_mod.popup_centered()
|
||||
create_mod.clear_mod_id_input()
|
||||
|
||||
|
||||
func _on_CreateMod_mod_dir_created() -> void:
|
||||
create_mod.hide()
|
||||
_update_ui()
|
||||
manifest_editor.load_manifest()
|
||||
manifest_editor.update_ui()
|
||||
|
||||
|
||||
func _on_ConnectMod_pressed() -> void:
|
||||
# Opens a popup that displays the mod directory names in the mods-unpacked directory
|
||||
select_mod.generate_dir_buttons(ModLoaderMod.get_unpacked_dir())
|
||||
select_mod.popup_centered()
|
||||
|
||||
|
||||
func _on_SelectMod_dir_selected(dir_path: String) -> void:
|
||||
var mod_dir_name := dir_path.split("/")[-1]
|
||||
load_mod(mod_dir_name)
|
||||
select_mod.hide()
|
||||
_update_ui()
|
||||
|
||||
|
||||
func _on_ButtonExportPath_pressed() -> void:
|
||||
file_dialog.current_path = mod_tool_store.path_export_dir
|
||||
file_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_FileDialog_dir_selected(dir: String) -> void:
|
||||
mod_tool_store.path_export_dir = dir
|
||||
export_path.input_text = dir
|
||||
file_dialog.hide()
|
||||
|
||||
|
||||
func _on_add_hooks_pressed() -> void:
|
||||
hook_gen.show()
|
||||
|
||||
|
||||
func _on_restore_pressed() -> void:
|
||||
hook_restore.show()
|
||||
|
||||
|
||||
func _on_hook_gen_hooks_exist_pressed() -> void:
|
||||
button_add_hooks.hide()
|
||||
button_restore.show()
|
1
addons/mod_tool/interface/panel/tools_panel.gd.uid
Normal file
1
addons/mod_tool/interface/panel/tools_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ckydauahlpir7
|
242
addons/mod_tool/interface/panel/tools_panel.tscn
Normal file
242
addons/mod_tool/interface/panel/tools_panel.tscn
Normal file
@@ -0,0 +1,242 @@
|
||||
[gd_scene load_steps=13 format=3 uid="uid://jgayt8758anm"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://glui2s46v4x4" path="res://addons/mod_tool/interface/create_mod/create_mod.tscn" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://hpefgw6k5qpq" path="res://addons/mod_tool/interface/manifest_editor/manifest_editor.tscn" id="2"]
|
||||
[ext_resource type="Script" path="res://addons/mod_tool/interface/panel/tools_panel.gd" id="3"]
|
||||
[ext_resource type="PackedScene" uid="uid://icwo58h0rdb5" path="res://addons/mod_tool/interface/global/input_string.tscn" id="4"]
|
||||
[ext_resource type="PackedScene" uid="uid://dyunxqcmy4esi" path="res://addons/mod_tool/interface/global/input_options.tscn" id="6"]
|
||||
[ext_resource type="PackedScene" uid="uid://du17jjwqtopix" path="res://addons/mod_tool/interface/global/directory_selection/select_directory.tscn" id="7"]
|
||||
[ext_resource type="PackedScene" path="res://addons/mod_tool/interface/global/input_string_with_button.tscn" id="8"]
|
||||
[ext_resource type="PackedScene" uid="uid://cpll5clcnemyj" path="res://addons/mod_tool/interface/hook_gen/hook_gen.tscn" id="8_k13cs"]
|
||||
[ext_resource type="PackedScene" uid="uid://camcc83bvu086" path="res://addons/mod_tool/interface/hook_restore/hook_restore.tscn" id="9_2cgta"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="14"]
|
||||
|
||||
[sub_resource type="Shortcut" id="12"]
|
||||
resource_name = "Copy Selection"
|
||||
|
||||
[sub_resource type="Shortcut" id="13"]
|
||||
resource_name = "Clear Output"
|
||||
|
||||
[node name="ModToolsPanel" type="Control"]
|
||||
custom_minimum_size = Vector2(0, 400)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -4.0
|
||||
offset_top = -6.0
|
||||
offset_right = 4.0
|
||||
offset_bottom = 4.0
|
||||
|
||||
[node name="VSplit" type="VSplitContainer" parent="Panel"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="Panel/VSplit"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("14")
|
||||
current_tab = 0
|
||||
|
||||
[node name="Manifest Editor" parent="Panel/VSplit/TabContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="Export" type="PanelContainer" parent="Panel/VSplit"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSplit" type="HSplitContainer" parent="Panel/VSplit/Export"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="Console" type="VBoxContainer" parent="Panel/VSplit/Export/HSplit"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Panel/VSplit/Export/HSplit/Console"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Panel/VSplit/Export/HSplit/Console/HBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Output:"
|
||||
|
||||
[node name="CopyOutput" type="Button" parent="Panel/VSplit/Export/HSplit/Console/HBox"]
|
||||
layout_mode = 2
|
||||
shortcut = SubResource("12")
|
||||
text = "Copy"
|
||||
|
||||
[node name="ClearOutput" type="Button" parent="Panel/VSplit/Export/HSplit/Console/HBox"]
|
||||
layout_mode = 2
|
||||
shortcut = SubResource("13")
|
||||
text = "Clear"
|
||||
|
||||
[node name="Output" type="RichTextLabel" parent="Panel/VSplit/Export/HSplit/Console"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
bbcode_enabled = true
|
||||
scroll_following = true
|
||||
selection_enabled = true
|
||||
|
||||
[node name="Settings" type="HBoxContainer" parent="Panel/VSplit/Export/HSplit"]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.5
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel/VSplit/Export/HSplit/Settings"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="Category" type="LineEdit" parent="Panel/VSplit/Export/HSplit/Settings/VBox"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
mouse_default_cursor_shape = 0
|
||||
text = "Export"
|
||||
editable = false
|
||||
context_menu_enabled = false
|
||||
virtual_keyboard_enabled = false
|
||||
shortcut_keys_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
|
||||
[node name="ModId" parent="Panel/VSplit/Export/HSplit/Settings/VBox" instance=ExtResource("4")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "ID of the mod to be exported.
|
||||
Format: Namespace-ModName
|
||||
(Often Author-ModName)"
|
||||
mouse_default_cursor_shape = 16
|
||||
is_editable = false
|
||||
input_text = "Test-Test"
|
||||
is_required = true
|
||||
label_text = "Mod ID"
|
||||
editor_icon_name = ""
|
||||
hint_text = "ID of the mod to be exported.
|
||||
Format: Namespace-ModName
|
||||
(Often Author-ModName)"
|
||||
|
||||
[node name="ExportType" parent="Panel/VSplit/Export/HSplit/Settings/VBox" instance=ExtResource("6")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
is_required = true
|
||||
key = "export_type"
|
||||
label_text = "Export Type"
|
||||
|
||||
[node name="ExportPath" parent="Panel/VSplit/Export/HSplit/Settings/VBox" instance=ExtResource("8")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "The directory to which the final mod zip is exported."
|
||||
mouse_default_cursor_shape = 16
|
||||
input_text = "C:/Users/Kai/Documents/godot/Brotato/mods/Mods/exports"
|
||||
is_required = true
|
||||
key = "path_export_dir"
|
||||
label_text = "Export Path"
|
||||
hint_text = "The directory to which the final mod zip is exported."
|
||||
|
||||
[node name="Align" type="HBoxContainer" parent="Panel/VSplit/Export/HSplit/Settings/VBox"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
alignment = 2
|
||||
|
||||
[node name="ExportStatus" type="Label" parent="Panel/VSplit/Export/HSplit/Settings/VBox/Align"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Export Status OK"
|
||||
|
||||
[node name="Export" type="Button" parent="Panel/VSplit/Export/HSplit/Settings/VBox/Align"]
|
||||
layout_mode = 2
|
||||
text = "Export Mod"
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel/VSplit/Export/HSplit"]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="Category" type="LineEdit" parent="Panel/VSplit/Export/HSplit/VBox"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
mouse_default_cursor_shape = 0
|
||||
text = "More Actions"
|
||||
editable = false
|
||||
context_menu_enabled = false
|
||||
virtual_keyboard_enabled = false
|
||||
shortcut_keys_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
|
||||
[node name="CreateMod" type="Button" parent="Panel/VSplit/Export/HSplit/VBox"]
|
||||
layout_mode = 2
|
||||
text = "Create new Mod"
|
||||
|
||||
[node name="ConnectMod" type="Button" parent="Panel/VSplit/Export/HSplit/VBox"]
|
||||
layout_mode = 2
|
||||
text = "Connect existing Mod"
|
||||
|
||||
[node name="AddHooks" type="Button" parent="Panel/VSplit/Export/HSplit/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Add Hooks to all Scripts"
|
||||
|
||||
[node name="Restore" type="Button" parent="Panel/VSplit/Export/HSplit/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Restore non Hooked Scripts"
|
||||
|
||||
[node name="CreateMod" parent="." instance=ExtResource("1")]
|
||||
unique_name_in_owner = true
|
||||
initial_position = 2
|
||||
size = Vector2i(400, 300)
|
||||
visible = false
|
||||
|
||||
[node name="SelectMod" parent="." instance=ExtResource("7")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="SelectModTemplate" parent="." instance=ExtResource("7")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="HookGen" parent="." instance=ExtResource("8_k13cs")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="HookRestore" parent="." instance=ExtResource("9_2cgta")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Open a Directory"
|
||||
initial_position = 1
|
||||
size = Vector2i(800, 500)
|
||||
ok_button_text = "Select Current Folder"
|
||||
file_mode = 2
|
||||
access = 2
|
||||
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/Console/HBox/CopyOutput" to="." method="_on_copy_output_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/Console/HBox/ClearOutput" to="." method="_on_clear_output_pressed"]
|
||||
[connection signal="button_pressed" from="Panel/VSplit/Export/HSplit/Settings/VBox/ExportPath" to="." method="_on_ButtonExportPath_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/Settings/VBox/Align/Export" to="." method="_on_export_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/VBox/CreateMod" to="." method="_on_export_settings_create_new_mod_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/VBox/ConnectMod" to="." method="_on_ConnectMod_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/VBox/AddHooks" to="." method="_on_add_hooks_pressed"]
|
||||
[connection signal="pressed" from="Panel/VSplit/Export/HSplit/VBox/Restore" to="." method="_on_restore_pressed"]
|
||||
[connection signal="mod_dir_created" from="CreateMod" to="." method="_on_CreateMod_mod_dir_created"]
|
||||
[connection signal="dir_selected" from="SelectMod" to="." method="_on_SelectMod_dir_selected"]
|
||||
[connection signal="hooks_exist_pressed" from="HookGen" to="." method="_on_hook_gen_hooks_exist_pressed"]
|
||||
[connection signal="dir_selected" from="FileDialog" to="." method="_on_FileDialog_dir_selected"]
|
Reference in New Issue
Block a user