└── addons └── config_table_manager.daylily-zeleen ├── scripts ├── table_header.gd ├── log.gd ├── import_modifier.gd ├── generate_modifier.gd └── preset.gd ├── scenes ├── meta_edit.gd ├── meta_edit.tscn ├── property_description_edit.gd ├── property_description_edit.tscn ├── gen_and_import.tscn ├── additional_property_edit.gd ├── settings.tscn ├── additional_property_edit.tscn ├── gen_and_import.gd ├── settings.gd ├── main_screen.gd └── main_screen.tscn ├── icon.svg.import ├── plugin.cfg ├── localization ├── localize.gd ├── template.pot └── en.po ├── config_table_manager.daylily-zeleen.gd ├── import_tools ├── import_tool.gd ├── gdscript_dictionary.gd └── gdscript_default.gd ├── table_tools ├── table_tool.gd ├── py │ └── xlsx_json.py ├── csv.gd └── xlsx.gd └── icon.svg /addons/config_table_manager.daylily-zeleen/scripts/table_header.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | 3 | var meta_list: PackedStringArray 4 | var descriptions: PackedStringArray 5 | var fields: PackedStringArray 6 | var types: PackedStringArray 7 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scripts/log.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | 3 | 4 | static func error(texts: PackedStringArray) -> void: 5 | printerr("[TableManager]", "".join(texts)) 6 | 7 | 8 | static func warning(texts: PackedStringArray) -> void: 9 | print_rich("[color=yellow][TableManager]%s[/color]" % "".join(texts)) 10 | 11 | 12 | static func info(texts: PackedStringArray) -> void: 13 | print("[TableManager]", "".join(texts)) 14 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | signal delete_request 5 | 6 | @onready var meta_line_edit: LineEdit = %MetaLineEdit 7 | @onready var delete_btn: Button = %DeleteBtn 8 | 9 | 10 | func _ready() -> void: 11 | delete_btn.pressed.connect(func() -> void: delete_request.emit()) 12 | 13 | 14 | func setup(meta: String) -> void: 15 | if not is_node_ready(): 16 | await ready 17 | meta_line_edit.text = meta 18 | 19 | 20 | func get_meta_text() -> String: 21 | return meta_line_edit.text 22 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dnjoxwe0301f6"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd" id="1_w8oww"] 4 | 5 | [node name="MetaEdit" type="PanelContainer"] 6 | offset_right = 1130.0 7 | offset_bottom = 44.0 8 | script = ExtResource("1_w8oww") 9 | 10 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 11 | layout_mode = 2 12 | 13 | [node name="MetaLineEdit" type="LineEdit" parent="HBoxContainer"] 14 | unique_name_in_owner = true 15 | layout_mode = 2 16 | size_flags_horizontal = 3 17 | placeholder_text = "Meta" 18 | 19 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"] 20 | unique_name_in_owner = true 21 | layout_mode = 2 22 | text = "X" 23 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | signal delete_request 5 | 6 | @onready var name_line_edit: LineEdit = %NameLineEdit 7 | @onready var description_edit: LineEdit = %DescriptionLineEdit 8 | @onready var delete_btn: Button = %DeleteBtn 9 | 10 | 11 | func _ready() -> void: 12 | delete_btn.pressed.connect(func() -> void: delete_request.emit()) 13 | preload("../localization/localize.gd").localize_node(self) 14 | 15 | func setup(prop_name: String, desc: String) -> void: 16 | if not is_node_ready(): 17 | await ready 18 | name_line_edit.text = prop_name 19 | description_edit.text = desc 20 | 21 | 22 | func get_property_name() -> String: 23 | return name_line_edit.text 24 | 25 | 26 | func get_description() -> String: 27 | return description_edit.text 28 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://itkw7iwcegvd"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd" id="1_16vph"] 4 | 5 | [node name="PropertyDescriptionEdit" type="PanelContainer"] 6 | offset_right = 1130.0 7 | offset_bottom = 44.0 8 | script = ExtResource("1_16vph") 9 | 10 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 11 | layout_mode = 2 12 | 13 | [node name="NameLineEdit" type="LineEdit" parent="HBoxContainer"] 14 | unique_name_in_owner = true 15 | layout_mode = 2 16 | size_flags_horizontal = 3 17 | size_flags_stretch_ratio = 0.25 18 | placeholder_text = "字段名称" 19 | 20 | [node name="DescriptionLineEdit" type="LineEdit" parent="HBoxContainer"] 21 | unique_name_in_owner = true 22 | layout_mode = 2 23 | size_flags_horizontal = 3 24 | placeholder_text = "描述" 25 | 26 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"] 27 | unique_name_in_owner = true 28 | layout_mode = 2 29 | text = " - " 30 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bkhw1qeqagwnl" 6 | path="res://.godot/imported/icon.svg-871927cf7bd84ef7791e78a3bd30c145.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/config_table_manager.daylily-zeleen/icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-871927cf7bd84ef7791e78a3bd30c145.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=1 24 | mipmaps/generate=true 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=false 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=0 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bepjlsptadkrq"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd" id="1_o1jft"] 4 | 5 | [node name="GenAndImport" type="VBoxContainer"] 6 | offset_right = 500.0 7 | offset_bottom = 647.0 8 | size_flags_horizontal = 3 9 | script = ExtResource("1_o1jft") 10 | title = "表格生成" 11 | 12 | [node name="TitleLabel" type="Label" parent="."] 13 | unique_name_in_owner = true 14 | layout_mode = 2 15 | text = "表格生成" 16 | 17 | [node name="Tree" type="Tree" parent="."] 18 | unique_name_in_owner = true 19 | layout_mode = 2 20 | size_flags_vertical = 3 21 | columns = 3 22 | column_titles_visible = true 23 | hide_root = true 24 | 25 | [node name="HBoxContainer2" type="HBoxContainer" parent="."] 26 | layout_mode = 2 27 | 28 | [node name="SelectOnlyBtn" type="Button" parent="HBoxContainer2"] 29 | unique_name_in_owner = true 30 | layout_mode = 2 31 | text = "对选中项进行生成" 32 | 33 | [node name="AllBtn" type="Button" parent="HBoxContainer2"] 34 | unique_name_in_owner = true 35 | layout_mode = 2 36 | text = "对所有项进行生成" 37 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | const _Localize = preload("../localization/localize.gd") 5 | 6 | signal delete_request 7 | 8 | @onready var name_line_edit: LineEdit = %NameLineEdit 9 | @onready var type_options: OptionButton = %TypeOptions 10 | @onready var setter_line_edit: LineEdit = %SetterLineEdit 11 | @onready var delete_btn: Button = %DeleteBtn 12 | 13 | 14 | func _ready() -> void: 15 | type_options.clear() 16 | for t in range(TYPE_MAX): 17 | if t in [TYPE_NIL, TYPE_OBJECT, TYPE_CALLABLE, TYPE_RID, TYPE_SIGNAL]: 18 | continue 19 | type_options.add_item(type_string(t).trim_prefix("Packed"), t) 20 | delete_btn.pressed.connect(func() -> void: delete_request.emit()) 21 | _Localize.localize_node(self) 22 | 23 | 24 | func setup(prop_name: String, type: int, setter: String) -> void: 25 | if not is_node_ready(): 26 | await ready 27 | name_line_edit.text = prop_name 28 | type_options.select(type_options.get_item_index(type)) 29 | setter_line_edit.text = setter 30 | 31 | 32 | func get_property_name() -> String: 33 | return name_line_edit.text 34 | 35 | 36 | func get_type() -> int: 37 | return type_options.get_selected_id() 38 | 39 | 40 | func get_setter() -> String: 41 | return setter_line_edit.text 42 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Config Table Manager" 4 | description="配置表格生成与导入工具。 5 | ## Features 6 | 7 | 1. Easy to use, generate table header by using a data class. Allow to add additional columns. 8 | 2. How to generate and import can be save as a preset, convenient to adjust repeatedly. 9 | 3. Support backup and merge when regenerating tables. 10 | 4. Highly customizable, you can add your **Table Tool** and **Import Tool** to generate table file and import as resource which are meet your needs. ( This plugin is provide **CSV**, **xlsx** table tools, and provide **GDScript** import tool.) 11 | 5. You can add Generate Modifier and Import Modifier to insert your logic for modify data in generating and importing workflow. 12 | 13 | ## 特性 14 | 15 | 1. 简单使用,根据数据类生成表头,允许添加附加列。 16 | 2. 生成与导入预设可保存,方便反复调节。 17 | 3. 支持重新生成表格时自动备份与合并。 18 | 4. 高度自定义,可添加自己的**表格工具**与**导入工具**,生成符合您需求的的表格文件或导入为你需要的资源格式。(该插件已提供**csv**,**xlsx**表格工具,以及**GDScript**导入工具。) 19 | 5. 可添加**生成修改器**与**导入修改器**,在表格生成与导入流程中插入你的自定义逻辑以修改要生成或导入的数据。 20 | 21 | 22 | others = \"诚接Godot游戏开发,欢迎邮件联系。/ Receive game development outsourcing, welcome to contact by email.\"" 23 | author="忘忧の (Daylily Zeleen) - daylily-zeleen@foxmail.com" 24 | version="0.3.0" 25 | script="config_table_manager.daylily-zeleen.gd" 26 | support_link = "https://afdian.com/a/Daylily-Zeleen" 27 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/localization/localize.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | 3 | static var _translations: Array[Translation] = [] 4 | 5 | 6 | static func add_translation(translation: Translation) -> void: 7 | if translation in _translations: 8 | return 9 | _translations.push_front(translation) 10 | 11 | 12 | static func remote_translation(translation: Translation) -> void: 13 | _translations.erase(translation) 14 | 15 | 16 | static func clean_translations() -> void: 17 | _translations.clear() 18 | 19 | 20 | static func translate(msg: String) -> String: 21 | var local := TranslationServer.get_tool_locale() 22 | for translation: Translation in _translations.filter(func(t: Translation) -> bool: return t.locale == local): 23 | translation = translation as Translation 24 | var translated := translation.get_message(msg) 25 | if not translated.is_empty() and translated != msg: 26 | return translated 27 | return msg 28 | 29 | 30 | static func localize_node(node: Control) -> void: 31 | for n in node.get_children(): 32 | if n is Control: 33 | localize_node(n) 34 | for p in node.get_property_list(): 35 | if not p.type in [TYPE_STRING, TYPE_STRING_NAME]: 36 | continue 37 | var name := p.name as String 38 | 39 | var val := node.get(name) as StringName 40 | if val.is_empty(): 41 | continue 42 | 43 | var translated := translate(val) 44 | if not translated.is_empty() and translated != val: 45 | node.set(name, translated) 46 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const _Localize = preload("res://addons/config_table_manager.daylily-zeleen/localization/localize.gd") 5 | var plugin_control: Control 6 | 7 | 8 | func _enter_tree() -> void: 9 | const localization_dir = "res://addons/config_table_manager.daylily-zeleen/localization" 10 | for file in DirAccess.get_files_at(localization_dir): 11 | if file.get_file().get_extension().to_lower() != "po": 12 | continue 13 | _Localize.add_translation(ResourceLoader.load(localization_dir.path_join(file), "", ResourceLoader.CACHE_MODE_IGNORE)) 14 | 15 | var path := _get_main_scene_path() 16 | plugin_control = ResourceLoader.load(path).instantiate() 17 | EditorInterface.get_editor_main_screen().add_child(plugin_control) 18 | EditorInterface.get_editor_main_screen().size_flags_vertical = Control.SIZE_EXPAND_FILL 19 | plugin_control.hide() 20 | 21 | 22 | func _exit_tree() -> void: 23 | _Localize.clean_translations() 24 | 25 | 26 | func _has_main_screen() -> bool: 27 | return true 28 | 29 | 30 | func _make_visible(visible: bool) -> void: 31 | plugin_control.visible = visible 32 | 33 | 34 | func _get_plugin_name() -> String: 35 | return "Config Table Manger" 36 | 37 | 38 | func _get_plugin_icon() -> Texture2D: 39 | return ResourceLoader.load(get_script().resource_path.get_base_dir().path_join("icon.svg"), "", ResourceLoader.CACHE_MODE_IGNORE) 40 | 41 | 42 | # ===================== 43 | func _get_main_scene_path() -> String: 44 | return get_script().resource_path.get_base_dir().path_join("scenes/main_screen.tscn") 45 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd: -------------------------------------------------------------------------------- 1 | ## 用于在导入流场中修改数据,内部状态不会被复用,每次修改修改流程都会实例化一个修改器 2 | @tool 3 | 4 | const _Localize = preload("../localization/localize.gd") 5 | 6 | 7 | # 修改开始时调用, 典型应用是用于让继承者知道当前是为哪个数据类进行数据修改 8 | func _begin_modify(_table_name: String, _data_class_name: String, _data_class_script: String) -> void: 9 | pass 10 | 11 | 12 | func _modify_table_name(table_name: String) -> String: 13 | return table_name 14 | 15 | 16 | func _modify_custom_setters(custom_setters: Dictionary) -> Dictionary: 17 | return custom_setters 18 | 19 | 20 | func _modify_data(data: Array[Dictionary]) -> Array[Dictionary]: 21 | return data 22 | 23 | 24 | func _modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray: 25 | return meta_list 26 | 27 | 28 | func _modify_import_tool_options(options: PackedStringArray) -> PackedStringArray: 29 | return options 30 | 31 | 32 | # ---------------------- 33 | func begin_modify(table_name: String, data_class_name: String, data_class_script: String) -> void: 34 | _begin_modify(table_name, data_class_name, data_class_script) 35 | 36 | 37 | func modify_table_name(table_name: String) -> String: 38 | return _modify_table_name(table_name) 39 | 40 | 41 | func modify_custom_setters(custom_setters: Dictionary) -> Dictionary: 42 | return _modify_custom_setters(custom_setters) 43 | 44 | 45 | func modify_data(data: Array[Dictionary]) -> Array[Dictionary]: 46 | return _modify_data(data) 47 | 48 | 49 | func modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray: 50 | if self.has_method("_modify_metas"): 51 | push_warning("'_modify_metas()' is deprecated, override '_modify_meta_list()' instead.") 52 | return self.call("_modify_metas", meta_list) 53 | else: 54 | return _modify_meta_list(meta_list) 55 | 56 | 57 | func modify_import_tool_options(options: PackedStringArray) -> PackedStringArray: 58 | return _modify_import_tool_options(options) 59 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd: -------------------------------------------------------------------------------- 1 | ## 表格导入工具基类,子类必须能够被无参构造 2 | ## 重写 _ 开头的方法已自定义您的导入逻辑 3 | @tool 4 | 5 | const _TableHeader = preload("../scripts/table_header.gd") 6 | const _Log = preload("../scripts/log.gd") 7 | const _Localize = preload("../localization/localize.gd") 8 | 9 | func _import( 10 | _import_path: String, 11 | _table_name: String, 12 | _header: _TableHeader, 13 | _data_class_name: String, 14 | _data_class_script: String, 15 | _instantiation: String, 16 | _custom_setters: Dictionary, 17 | _data_rows: Array[Dictionary], 18 | _options: PackedStringArray 19 | ) -> Error: 20 | assert(false, "Unimplemented") 21 | _Log.error(["Unimplemented: _import"]) 22 | return FAILED 23 | 24 | 25 | func _get_import_file_extension() -> String: 26 | assert(false, "Unimplemented") 27 | _Log.error(["Unimplemented: _get_import_file_extension"]) 28 | return "" 29 | 30 | 31 | func _get_tooltip_text() -> String: 32 | return "" 33 | 34 | #----------------------- 35 | func is_meta_filed(field_name: String) -> bool: 36 | return field_name.begins_with("#") 37 | 38 | 39 | func to_type_id(type_text: String) -> int: 40 | type_text = type_text.strip_edges() 41 | for i in range(TYPE_MAX): 42 | if type_text == type_string(i): 43 | return i 44 | return -1 45 | 46 | 47 | func import( 48 | import_path: String, 49 | table_name: String, 50 | header: _TableHeader, 51 | data_class_name: String, 52 | data_class_script: String, 53 | instantiation: String, 54 | custom_setters: Dictionary, 55 | data_rows: Array[Dictionary], 56 | options: PackedStringArray 57 | ) -> Error: 58 | return _import(import_path, table_name, header, data_class_name, data_class_script, instantiation, custom_setters, data_rows, options) 59 | 60 | 61 | func get_import_file_extension() -> String: 62 | return _get_import_file_extension() 63 | 64 | 65 | #region Tools 66 | func get_tooltip_text() -> String: 67 | return _get_tooltip_text() 68 | 69 | 70 | func get_kv_option(option: String) -> Dictionary: 71 | var splits := option.split("=", false) 72 | assert(splits.size() == 2, "\"%s\" is not a valid key-value option text." % option) 73 | var ret := {} 74 | ret[splits[0].strip_edges()] = splits[1].strip_edges() 75 | return ret 76 | 77 | 78 | func parse_options(options: PackedStringArray) -> Dictionary: 79 | var ret := {} 80 | for op in options: 81 | if "=" in op: 82 | ret.merge(get_kv_option(op)) 83 | else: 84 | ret[op.strip_edges()] = null 85 | return ret 86 | 87 | #endregion Tools 88 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd: -------------------------------------------------------------------------------- 1 | ## 在生成表格流程中修改数据,内部状态不会被复用,每次修改都会实例化一个修改器, 2 | @tool 3 | 4 | const _Localize = preload("../localization/localize.gd") 5 | 6 | 7 | # 修改开始时调用, 典型应用是用于让继承者知道当前是为哪个数据类进行数据修改 8 | func _begin_modify(_table_name: String, _data_class_name: String, _data_class_script: String) -> void: 9 | pass 10 | 11 | 12 | ## 修改后的字段与类型数量必须匹配,且类型必须时表格工具支持的类型 13 | func _modify_fields_definitions(_in_out_fields: PackedStringArray, _in_out_types: PackedByteArray) -> void: 14 | pass 15 | 16 | 17 | func _modify_data(data: Array[Dictionary]) -> Array[Dictionary]: 18 | return data 19 | 20 | 21 | ## descriptions: Key 为字段名(String), Value 为描述(String) 22 | func _modify_descriptions(descriptions: Dictionary) -> Dictionary: 23 | return descriptions 24 | 25 | 26 | func _modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray: 27 | return meta_list 28 | 29 | 30 | func _modify_table_tool_options(options: PackedStringArray) -> PackedStringArray: 31 | return options 32 | 33 | 34 | # -------------------------- 35 | func begin_modify(table_name: String, data_class_name: String, data_class_script: String) -> void: 36 | _begin_modify(table_name, data_class_name, data_class_script) 37 | 38 | 39 | func modify_fields_definitions(in_out_fields: PackedStringArray, in_out_types: PackedByteArray) -> bool: 40 | if self.has_method("_modify_fileds_definitions"): 41 | push_warning("'_modify_fileds_definitions()' is deprecated, override '_modify_fields_definitions()' instead.") 42 | self.call("_modify_fileds_definitions", in_out_fields, in_out_types) 43 | else: 44 | _modify_fields_definitions(in_out_fields, in_out_types) 45 | if in_out_fields.size() != in_out_types.size(): 46 | preload("log.gd").error([_Localize.translate(&"修改后的字段数量与类型属性不对应。")]) 47 | return false 48 | return true 49 | 50 | 51 | func modify_data(data: Array[Dictionary]) -> Array[Dictionary]: 52 | return _modify_data(data) 53 | 54 | 55 | ## descriptions: Key 为字段名(String), Value 为描述(String) 56 | func modify_descriptions(descriptions: Dictionary) -> Dictionary: 57 | return _modify_descriptions(descriptions) 58 | 59 | 60 | func modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray: 61 | if self.has_method("_modify_metas"): 62 | push_warning("'_modify_metas()' is deprecated, override '_modify_meta_list()' instead.") 63 | return self.call("_modify_metas", meta_list) 64 | else: 65 | return _modify_meta_list(meta_list) 66 | 67 | 68 | func modify_table_tool_options(options: PackedStringArray) -> PackedStringArray: 69 | return _modify_table_tool_options(options) 70 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/settings.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bwb7n8a3xx0bu"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd" id="1_klie4"] 4 | 5 | [node name="设置" type="PanelContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | size_flags_horizontal = 3 12 | size_flags_vertical = 3 13 | script = ExtResource("1_klie4") 14 | table_tools = { 15 | "CSV(,分隔)": "res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd" 16 | } 17 | import_tools = { 18 | "默认GDScript导入": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd" 19 | } 20 | metadata/_tab_index = 1 21 | 22 | [node name="MarginContainer" type="MarginContainer" parent="."] 23 | layout_mode = 2 24 | theme_override_constants/margin_left = 20 25 | theme_override_constants/margin_top = 20 26 | theme_override_constants/margin_right = 20 27 | theme_override_constants/margin_bottom = 20 28 | 29 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 30 | layout_mode = 2 31 | 32 | [node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer"] 33 | layout_mode = 2 34 | columns = 3 35 | 36 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/GridContainer"] 37 | layout_mode = 2 38 | text = "预设保存路径:" 39 | 40 | [node name="PresetSaveDirLineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/GridContainer"] 41 | unique_name_in_owner = true 42 | layout_mode = 2 43 | size_flags_horizontal = 3 44 | text = "res://tables/presets/" 45 | caret_blink = true 46 | caret_blink_interval = 0.5 47 | 48 | [node name="PresetDirSelectBtn" type="Button" parent="MarginContainer/VBoxContainer/GridContainer"] 49 | unique_name_in_owner = true 50 | layout_mode = 2 51 | text = "选择" 52 | 53 | [node name="TableToolsTree" type="Tree" parent="MarginContainer/VBoxContainer"] 54 | unique_name_in_owner = true 55 | layout_mode = 2 56 | size_flags_vertical = 3 57 | columns = 3 58 | column_titles_visible = true 59 | hide_root = true 60 | 61 | [node name="ImportToolsTree" type="Tree" parent="MarginContainer/VBoxContainer"] 62 | unique_name_in_owner = true 63 | layout_mode = 2 64 | size_flags_vertical = 3 65 | columns = 3 66 | column_titles_visible = true 67 | hide_root = true 68 | 69 | [node name="BoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 70 | layout_mode = 2 71 | size_flags_vertical = 10 72 | alignment = 1 73 | 74 | [node name="SaveBtn" type="Button" parent="MarginContainer/VBoxContainer/BoxContainer"] 75 | unique_name_in_owner = true 76 | layout_mode = 2 77 | text = "保存设置" 78 | 79 | [node name="FileDialog" type="FileDialog" parent="."] 80 | unique_name_in_owner = true 81 | title = "Open a Directory" 82 | position = Vector2i(384, 216) 83 | size = Vector2i(1152, 648) 84 | ok_button_text = "选择当前文件夹" 85 | file_mode = 2 86 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ckg7862ktuel8"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd" id="1_nbwpx"] 4 | 5 | [node name="AdditionalPropertyEdit" type="PanelContainer"] 6 | offset_right = 1130.0 7 | offset_bottom = 44.0 8 | script = ExtResource("1_nbwpx") 9 | 10 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 11 | layout_mode = 2 12 | 13 | [node name="NameLineEdit" type="LineEdit" parent="HBoxContainer"] 14 | unique_name_in_owner = true 15 | layout_mode = 2 16 | size_flags_horizontal = 3 17 | size_flags_stretch_ratio = 0.49 18 | placeholder_text = "要附加的属性名称" 19 | 20 | [node name="TypeOptions" type="OptionButton" parent="HBoxContainer"] 21 | unique_name_in_owner = true 22 | custom_minimum_size = Vector2(120, 0) 23 | layout_mode = 2 24 | item_count = 33 25 | popup/item_0/text = "bool" 26 | popup/item_0/id = 1 27 | popup/item_1/text = "int" 28 | popup/item_1/id = 2 29 | popup/item_2/text = "float" 30 | popup/item_2/id = 3 31 | popup/item_3/text = "String" 32 | popup/item_3/id = 4 33 | popup/item_4/text = "Vector2" 34 | popup/item_4/id = 5 35 | popup/item_5/text = "Vector2i" 36 | popup/item_5/id = 6 37 | popup/item_6/text = "Rect2" 38 | popup/item_6/id = 7 39 | popup/item_7/text = "Rect2i" 40 | popup/item_7/id = 8 41 | popup/item_8/text = "Vector3" 42 | popup/item_8/id = 9 43 | popup/item_9/text = "Vector3i" 44 | popup/item_9/id = 10 45 | popup/item_10/text = "Transform2D" 46 | popup/item_10/id = 11 47 | popup/item_11/text = "Vector4" 48 | popup/item_11/id = 12 49 | popup/item_12/text = "Vector4i" 50 | popup/item_12/id = 13 51 | popup/item_13/text = "Plane" 52 | popup/item_13/id = 14 53 | popup/item_14/text = "Quaternion" 54 | popup/item_14/id = 15 55 | popup/item_15/text = "AABB" 56 | popup/item_15/id = 16 57 | popup/item_16/text = "Basis" 58 | popup/item_16/id = 17 59 | popup/item_17/text = "Transform3D" 60 | popup/item_17/id = 18 61 | popup/item_18/text = "Projection" 62 | popup/item_18/id = 19 63 | popup/item_19/text = "Color" 64 | popup/item_19/id = 20 65 | popup/item_20/text = "StringName" 66 | popup/item_20/id = 21 67 | popup/item_21/text = "NodePath" 68 | popup/item_21/id = 22 69 | popup/item_22/text = "Dictionary" 70 | popup/item_22/id = 27 71 | popup/item_23/text = "Array" 72 | popup/item_23/id = 28 73 | popup/item_24/text = "ByteArray" 74 | popup/item_24/id = 29 75 | popup/item_25/text = "Int32Array" 76 | popup/item_25/id = 30 77 | popup/item_26/text = "Int64Array" 78 | popup/item_26/id = 31 79 | popup/item_27/text = "Float32Array" 80 | popup/item_27/id = 32 81 | popup/item_28/text = "Float64Array" 82 | popup/item_28/id = 33 83 | popup/item_29/text = "StringArray" 84 | popup/item_29/id = 34 85 | popup/item_30/text = "Vector2Array" 86 | popup/item_30/id = 35 87 | popup/item_31/text = "Vector3Array" 88 | popup/item_31/id = 36 89 | popup/item_32/text = "ColorArray" 90 | popup/item_32/id = 37 91 | 92 | [node name="SetterLineEdit" type="LineEdit" parent="HBoxContainer"] 93 | unique_name_in_owner = true 94 | layout_mode = 2 95 | size_flags_horizontal = 3 96 | placeholder_text = "非空将使用自定义Setter进行设置" 97 | 98 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"] 99 | unique_name_in_owner = true 100 | layout_mode = 2 101 | text = " - " 102 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends VBoxContainer 3 | 4 | const _Preset = preload("../scripts/preset.gd") 5 | const _Log = _Preset._Log 6 | const _Localize = _Preset._Localize 7 | 8 | enum Mode { 9 | GENERATE_TABLE, 10 | IMPORT_TABLE, 11 | } 12 | 13 | @export var title: String: 14 | set(v): 15 | if not is_node_ready(): 16 | await ready 17 | %TitleLabel.text = v 18 | get: 19 | return %TitleLabel.text 20 | 21 | @export var mode: Mode: 22 | set(v): 23 | mode = v 24 | if mode == Mode.GENERATE_TABLE: 25 | %SelectOnlyBtn.text = _Localize.translate("对选中项进行生成") 26 | %AllBtn.text = _Localize.translate("对所有项进行生成") 27 | else: 28 | %SelectOnlyBtn.text = _Localize.translate("对选中项执行导入") 29 | %AllBtn.text = _Localize.translate("对所有项执行导入") 30 | 31 | var save_path: String 32 | 33 | @onready var _tree: Tree = %Tree 34 | 35 | 36 | func _ready() -> void: 37 | %SelectOnlyBtn.pressed.connect(_on_select_only_btn_pressed) 38 | %AllBtn.pressed.connect(_on_all_btn_pressed) 39 | 40 | mode = mode 41 | 42 | _tree.clear() 43 | _tree.create_item() 44 | _tree.column_titles_visible = true 45 | _tree.set_column_title(0, _Localize.translate("选中")) 46 | _tree.set_column_title(1, _Localize.translate("排除")) 47 | _tree.set_column_title(2, _Localize.translate("预设")) 48 | _tree.set_column_expand(0, false) 49 | _tree.set_column_expand(1, false) 50 | _tree.item_edited.connect(_on_tree_item_edited) 51 | 52 | 53 | func setup(presets: Array[_Preset]) -> void: 54 | for item in _tree.get_root().get_children(): 55 | _tree.get_root().remove_child(item) 56 | item.free() 57 | 58 | for p in presets: 59 | var item := _tree.get_root().create_child() as TreeItem 60 | item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK) 61 | item.set_cell_mode(1, TreeItem.CELL_MODE_CHECK) 62 | item.set_text(2, "%s (%s)" % [p.name, p.resource_path]) 63 | item.set_editable(0, true) 64 | item.set_editable(1, true) 65 | item.set_metadata(2, p.resource_path) 66 | 67 | _load_archive() 68 | 69 | 70 | # ----------------------- 71 | func _on_select_only_btn_pressed() -> void: 72 | _execute(true) 73 | 74 | 75 | func _on_all_btn_pressed() -> void: 76 | _execute(false) 77 | 78 | 79 | func _on_tree_item_edited() -> void: 80 | if save_path.is_empty(): 81 | return 82 | 83 | var presets := {} 84 | 85 | for item in _tree.get_root().get_children(): 86 | if not item.is_checked(0) and not item.is_checked(1): 87 | continue 88 | var preset_file := item.get_metadata(2) as String 89 | if not presets.has(preset_file): 90 | presets[preset_file] = {} 91 | if item.is_checked(0): 92 | presets[preset_file]["include"] = true 93 | if item.is_checked(1): 94 | presets[preset_file]["exclude"] = true 95 | 96 | if not DirAccess.dir_exists_absolute(save_path.get_base_dir()): 97 | DirAccess.make_dir_recursive_absolute(save_path.get_base_dir()) 98 | 99 | var f := FileAccess.open(save_path, FileAccess.WRITE) 100 | if not is_instance_valid(f): 101 | printerr("BUG, Open File failed: ", error_string(FileAccess.get_open_error())) 102 | return 103 | 104 | f.store_var(presets) 105 | f.close() 106 | 107 | 108 | # ------------- 109 | func _load_archive() -> void: 110 | if not FileAccess.file_exists(save_path): 111 | return 112 | 113 | var f := FileAccess.open(save_path, FileAccess.READ) 114 | if not is_instance_valid(f): 115 | printerr("BUG, open file failed: ", error_string(FileAccess.get_open_error())) 116 | return 117 | 118 | var presets: Variant = f.get_var() 119 | if typeof(presets) != TYPE_DICTIONARY: 120 | printerr("BUG, Parse error") 121 | return 122 | 123 | for item in _tree.get_root().get_children(): 124 | var preset_file := item.get_metadata(2) as String 125 | 126 | if not presets.has(preset_file): 127 | continue 128 | 129 | item.set_checked(0, presets[preset_file].get("include", false)) 130 | item.set_checked(1, presets[preset_file].get("exclude", false)) 131 | 132 | 133 | func _execute(select_only: bool) -> void: 134 | for item in _tree.get_root().get_children(): 135 | if select_only and not item.is_checked(0): 136 | # 仅选中项 137 | continue 138 | if item.is_checked(1): 139 | # 排除 140 | continue 141 | var path := item.get_metadata(2) as String 142 | var preset := ResourceLoader.load(path, "Resource", ResourceLoader.CACHE_MODE_IGNORE) as _Preset 143 | if not is_instance_valid(preset): 144 | _Log.error([_Localize.translate("表格预设不存在: "), path]) 145 | continue 146 | 147 | if preset.resource_path.is_empty(): 148 | preset.take_over_path(path) 149 | 150 | if mode == Mode.GENERATE_TABLE: 151 | preset.generate_table() 152 | else: 153 | preset.import_table() 154 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd: -------------------------------------------------------------------------------- 1 | ## 表格工具基类,子类必须能够被无参构造 2 | ## 重写 _ 开头的方法已自定义您的解析表格与生成表格逻辑 3 | @tool 4 | 5 | const _Log = preload("../scripts/log.gd") 6 | const _TableHeader = preload("../scripts/table_header.gd") 7 | const _Localize = preload("../localization/localize.gd") 8 | 9 | 10 | func _get_support_types() -> PackedByteArray: 11 | assert(false, "unimplemented") 12 | _Log.error(["Unimplemented: _get_support_types"]) 13 | return [] 14 | 15 | 16 | func _get_parse_error() -> Error: 17 | assert(false, "unimplemented") 18 | _Log.error(["Unimplemented: _get_parse_error"]) 19 | return ERR_PARSE_ERROR 20 | 21 | 22 | func _parse_table_file(_table_file: String, _options: PackedStringArray) -> Error: 23 | assert(false, "unimplemented") 24 | _Log.error(["Unimplemented: _parse_table_file"]) 25 | return ERR_PARSE_ERROR 26 | 27 | 28 | func _get_table_file_extension() -> String: 29 | assert(false, "unimplemented") 30 | _Log.error(["Unimplemented: _get_table_file_extension"]) 31 | return "" 32 | 33 | 34 | func _generate_table_file(_save_path: String, _table_header: _TableHeader, _data_rows: Array[PackedStringArray], _options: PackedStringArray) -> Error: 35 | assert(false, "unimplemented") 36 | _Log.error(["Unimplemented: _generate_table_file"]) 37 | return FAILED 38 | 39 | 40 | func _to_value_text(_value: Variant) -> String: 41 | assert(false, "unimplemented") 42 | _Log.error(["Unimplemented: _to_value_text"]) 43 | return "" 44 | 45 | 46 | func _parse_value(_text: String, _type_id: int) -> Variant: 47 | assert(false, "unimplemented") 48 | _Log.error(["Unimplemented: _parse_value"]) 49 | return null 50 | 51 | 52 | # 获取解析结果->表头 53 | func _get_header() -> _TableHeader: 54 | assert(false, "unimplemented") 55 | _Log.error(["Unimplemented: _get_header"]) 56 | return null 57 | 58 | 59 | # 获取解析结果->数据 60 | func _get_data() -> Array[Dictionary]: 61 | assert(false, "unimplemented") 62 | _Log.error(["Unimplemented: _get_data"]) 63 | return [] 64 | 65 | 66 | func _get_tooltip_text() -> String: 67 | return "" 68 | 69 | # ----------- 70 | func is_meta_filed(field_name: String) -> bool: 71 | return field_name.begins_with("#") 72 | 73 | 74 | func get_type_id(type_text: String) -> int: 75 | type_text = type_text.strip_edges() 76 | for t in _get_support_types(): 77 | if type_string(t) == type_text: 78 | return t 79 | _Log.error([_Localize.translate("不支持的类型: "), type_text]) 80 | return -1 81 | 82 | 83 | func to_data_rows(data: Array[Dictionary], fields: PackedStringArray, types: PackedByteArray) -> Array[PackedStringArray]: 84 | if fields.size() != types.size(): 85 | _Log.error([_Localize.translate("无法转换,类型与字段不符")]) 86 | return [] 87 | 88 | for t in types: 89 | if not t in _get_support_types(): 90 | _Log.error([_Localize.translate("无法转换,不支持的类型: "), type_string(t)]) 91 | return [] 92 | 93 | var ret: Array[PackedStringArray] = [] 94 | for d in data: 95 | var r: PackedStringArray = [] 96 | r.resize(types.size()) 97 | for i in range(fields.size()): 98 | var f := fields[i].strip_edges() 99 | var t := types[i] 100 | if not f in d: 101 | continue 102 | 103 | r[i] = _to_value_text(type_convert(d[f], t)) 104 | ret.push_back(r) 105 | 106 | return ret 107 | 108 | 109 | func get_support_types() -> PackedByteArray: 110 | return _get_support_types() 111 | 112 | 113 | func get_parse_error() -> Error: 114 | return _get_parse_error() 115 | 116 | 117 | func parse_table_file(file: String, options: PackedStringArray) -> Error: 118 | return _parse_table_file(file, options) 119 | 120 | 121 | func get_table_file_extension() -> String: 122 | return _get_table_file_extension() 123 | 124 | 125 | func generate_table_file(save_path: String, table_header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error: 126 | return _generate_table_file(save_path, table_header, data_rows, options) 127 | 128 | 129 | func to_value_text(value: Variant) -> String: 130 | return _to_value_text(value) 131 | 132 | 133 | func parse_value(text: String, type_id: int) -> Variant: 134 | return _parse_value(text, type_id) 135 | 136 | 137 | func get_header() -> _TableHeader: 138 | return _get_header() 139 | 140 | 141 | func get_data() -> Array[Dictionary]: 142 | return _get_data() 143 | 144 | 145 | #region Tools 146 | func get_tooltip_text() -> String: 147 | return _get_tooltip_text() 148 | 149 | 150 | func get_kv_option(option: String) -> Dictionary: 151 | var splits := option.split("=", false) 152 | assert(splits.size() == 2, "\"%s\" is not a valid key-value option text." % option) 153 | var ret := {} 154 | ret[splits[0].strip_edges()] = splits[1].strip_edges() 155 | return ret 156 | 157 | 158 | func parse_options(options: PackedStringArray) -> Dictionary: 159 | var ret := {} 160 | for op in options: 161 | if "=" in op: 162 | ret.merge(get_kv_option(op)) 163 | else: 164 | ret[op.strip_edges()] = null 165 | return ret 166 | 167 | #endregion Tools 168 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 34 | 37 | 44 | 52 | 59 | 66 | 73 | 80 | 87 | 94 | 102 | 111 | 120 | 129 | 138 | 147 | 156 | 157 | 167 | 168 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/settings.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | const _Localize = preload("../localization/localize.gd") 5 | 6 | signal tools_updated 7 | 8 | var presets_dir: String: 9 | set(v): 10 | _preset_save_dir_line_edit.text = v 11 | get: 12 | return _preset_save_dir_line_edit.text 13 | 14 | @export var table_tools: Dictionary: 15 | set(v): 16 | table_tools = v 17 | tools_updated.emit() 18 | get: 19 | return _validate_tools(table_tools, true) 20 | @export var import_tools: Dictionary: 21 | set(v): 22 | import_tools = v 23 | tools_updated.emit() 24 | get: 25 | return _validate_tools(import_tools, false) 26 | 27 | @onready var _preset_save_dir_line_edit: LineEdit = %PresetSaveDirLineEdit 28 | @onready var _preset_dir_select_btn: Button = %PresetDirSelectBtn 29 | 30 | @onready var _table_tools_tree: Tree = %TableToolsTree 31 | @onready var _import_tools_tree: Tree = %ImportToolsTree 32 | 33 | @onready var _save_btn: Button = %SaveBtn 34 | @onready var _file_dialog: FileDialog = %FileDialog 35 | 36 | const DEFAULT_TABLE_TOOL = { 37 | "CSV(,分隔)": "res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd", 38 | "Excel(xlsx)": "res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd", 39 | } 40 | 41 | const DEFAULT_IMPORT_TOOL = { 42 | "GDScript(TypedArray风格)": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd", 43 | "GDScript(Dictionary风格)": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd", 44 | } 45 | 46 | 47 | func _ready() -> void: 48 | _preset_dir_select_btn.pressed.connect(func() -> void: _file_dialog.popup_centered_ratio(0.6)) 49 | _file_dialog.dir_selected.connect(func(dir: String) -> void: _preset_save_dir_line_edit.text = dir) 50 | _save_btn.pressed.connect(_on_save_btn_pressed) 51 | 52 | _table_tools_tree.clear() 53 | _table_tools_tree.create_item() 54 | _table_tools_tree.set_column_title(0, _Localize.translate("表格工具")) 55 | _table_tools_tree.set_column_title(1, _Localize.translate("脚本路径")) 56 | _table_tools_tree.set_column_title(2, _Localize.translate("+")) 57 | _table_tools_tree.set_column_expand(1, true) 58 | _table_tools_tree.set_column_expand(2, false) 59 | _table_tools_tree.column_title_clicked.connect(_on_tree_column_title_clicked.bind(_table_tools_tree)) 60 | _table_tools_tree.item_edited.connect(_on_tree_item_edited.bind(_table_tools_tree)) 61 | _table_tools_tree.button_clicked.connect(_on_tree_button_clicked.bind(_table_tools_tree)) 62 | 63 | _import_tools_tree.clear() 64 | _import_tools_tree.create_item() 65 | _import_tools_tree.set_column_title(0, _Localize.translate("导入工具")) 66 | _import_tools_tree.set_column_title(1, _Localize.translate("脚本路径")) 67 | _import_tools_tree.set_column_title(2, _Localize.translate("+")) 68 | _import_tools_tree.set_column_expand(1, true) 69 | _import_tools_tree.set_column_expand(2, false) 70 | _import_tools_tree.column_title_clicked.connect(_on_tree_column_title_clicked.bind(_import_tools_tree)) 71 | _import_tools_tree.item_edited.connect(_on_tree_item_edited.bind(_import_tools_tree)) 72 | _import_tools_tree.button_clicked.connect(_on_tree_button_clicked.bind(_import_tools_tree)) 73 | 74 | _refresh() 75 | 76 | 77 | func _refresh() -> void: 78 | var root := _table_tools_tree.get_root() 79 | for item in root.get_children(): 80 | root.remove_child(item) 81 | item.free() 82 | for k: String in DEFAULT_TABLE_TOOL: 83 | _add_tree_item(root, k, DEFAULT_TABLE_TOOL[k], false) 84 | for n: String in table_tools: 85 | if table_tools[n] in DEFAULT_TABLE_TOOL.values(): 86 | continue 87 | _add_tree_item(root, n, table_tools[n]) 88 | _refresh_tools(_table_tools_tree) 89 | 90 | root = _import_tools_tree.get_root() 91 | for item in root.get_children(): 92 | root.remove_child(item) 93 | item.free() 94 | for k: String in DEFAULT_IMPORT_TOOL: 95 | _add_tree_item(root, k, DEFAULT_IMPORT_TOOL[k], false) 96 | for n: String in import_tools: 97 | if import_tools[n] in DEFAULT_IMPORT_TOOL.values(): 98 | continue 99 | _add_tree_item(root, n, import_tools[n]) 100 | _refresh_tools(_import_tools_tree) 101 | 102 | 103 | func _add_tree_item(parent: TreeItem, p_name: String, path: String, editable := true) -> void: 104 | var item := parent.create_child() 105 | item.set_text(0, _Localize.translate(p_name)) 106 | item.set_text(1, path) 107 | item.set_editable(0, editable) 108 | item.set_editable(1, editable) 109 | 110 | if editable: 111 | item.add_button(2, EditorInterface.get_editor_theme().get_icon(&"Remove", &"EditorIcons"), 0) 112 | item.set_text(2, "-") 113 | 114 | 115 | func _refresh_tools(tree: Tree) -> void: 116 | var map := {} 117 | for item in tree.get_root().get_children(): 118 | var n := item.get_text(0).strip_edges() 119 | if map.has(n): 120 | continue 121 | map[n] = item.get_text(1) 122 | 123 | if tree == _table_tools_tree: 124 | table_tools = map 125 | elif tree == _import_tools_tree: 126 | import_tools = map 127 | 128 | 129 | func _validate_tools(tools: Dictionary, for_table_tool: bool) -> Dictionary: 130 | var ret := {} 131 | var required_base: Script 132 | if for_table_tool: 133 | required_base = ResourceLoader.load("res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd", "Script", ResourceLoader.CACHE_MODE_IGNORE) 134 | else: 135 | required_base = ResourceLoader.load("res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd", "Script", ResourceLoader.CACHE_MODE_IGNORE) 136 | 137 | for n: String in tools: 138 | n = n.strip_edges() 139 | var path := tools[n] as String 140 | if n.is_empty(): 141 | continue 142 | if not ResourceLoader.exists(path, "Script"): 143 | continue 144 | var s := ResourceLoader.load(path, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script 145 | if not is_instance_valid(s): 146 | continue 147 | 148 | var valid := false 149 | var base := s.get_base_script() 150 | while base: 151 | if base == required_base: 152 | valid = true 153 | break 154 | base = base.get_base_script() 155 | 156 | if valid: 157 | ret[n] = path 158 | 159 | return ret 160 | 161 | 162 | # -------- 163 | func _on_tree_column_title_clicked(column: int, mouse_button_index: int, tree: Tree) -> void: 164 | if column != 2 or mouse_button_index != MOUSE_BUTTON_LEFT: 165 | return 166 | _add_tree_item(tree.get_root(), "tool name", "script path") 167 | 168 | 169 | func _on_tree_item_edited(tree: Tree) -> void: 170 | _refresh_tools(tree) 171 | 172 | 173 | func _on_tree_button_clicked(item: TreeItem, column: int, id: int, mouse_button_index: int, tree: Tree) -> void: 174 | if column != 2 or mouse_button_index != MOUSE_BUTTON_LEFT or id != 0: 175 | return 176 | if not item.is_editable(0): 177 | return 178 | item.get_parent().remove_child(item) 179 | item.free() 180 | 181 | _refresh_tools(tree) 182 | 183 | 184 | func _on_save_btn_pressed() -> void: 185 | var scene_path := (get_script().resource_path as String).trim_suffix("gd") + "tscn" 186 | var ps := PackedScene.new() 187 | var err := ps.pack(self) 188 | if err != OK: 189 | printerr(_Localize.translate("保存设置失败: "), error_string(err)) 190 | 191 | err = ResourceSaver.save(ps, scene_path) 192 | if err != OK: 193 | printerr(_Localize.translate("保存设置失败: "), error_string(err)) 194 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/table_tools/py/xlsx_json.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import logging 3 | 4 | # 检查 5 | _result = subprocess.run("pip show openpyxl", shell=True, capture_output=True) 6 | if _result.returncode != 0: 7 | logging.error("This tool is require 'openpyxl' package, please run: pip install openpyxl") 8 | exit(1) 9 | 10 | 11 | import sys 12 | import json 13 | import os 14 | import openpyxl 15 | from openpyxl import Workbook, cell 16 | from openpyxl.styles.colors import Color, RGB 17 | from openpyxl.styles.borders import Border, Side 18 | 19 | 20 | def dump_xlsx_to_json(xlsx_file_path: str, output_json_path: str): 21 | workbook: Workbook = openpyxl.load_workbook(xlsx_file_path) 22 | 23 | data: dict[str, dict] = {} 24 | 25 | for sheet_name in workbook.sheetnames: 26 | sheet = workbook[sheet_name] 27 | # Sheet 28 | sheet_data: list[list[dict[str, None]]] = [] 29 | for row in range(sheet.max_row): 30 | row_data: list[dict] = [] 31 | for column in range(sheet.max_column): 32 | cell = sheet.cell(row=row + 1, column=column + 1) 33 | row_data.append( 34 | { 35 | "value": cell.value, 36 | "fill": _to_dict(cell.fill), 37 | "border": _to_dict(cell.border), 38 | # TODO: 存储更多的单元格属性 39 | } 40 | ) 41 | sheet_data.append(row_data) 42 | 43 | data[sheet_name] = { 44 | "data": sheet_data, 45 | "sheet_format": _to_dict(sheet.sheet_format), 46 | # TODO: 存储更多的Sheet属性 47 | } 48 | 49 | workbook.close() 50 | f = open(output_json_path, "w", encoding="utf8") 51 | json.dump(obj=data, fp=f, indent="\t") 52 | f.close() 53 | return True 54 | 55 | 56 | def override_xlsx_worksheets_from_json(json_file_path: str, output_xlsx_file_path: str): 57 | f = open(json_file_path, "r", encoding="utf8") 58 | data: dict[str, dict] = json.load(f) 59 | 60 | if len(data) <= 0: 61 | logging.error("The input json has not data.") 62 | exit(1) 63 | 64 | workbook: Workbook 65 | default_sheetnames: list[str] = [] 66 | if os.path.exists(output_xlsx_file_path): 67 | workbook = openpyxl.load_workbook(output_xlsx_file_path) 68 | else: 69 | workbook = Workbook() 70 | default_sheetnames = workbook.sheetnames 71 | 72 | for sheet_name in data: 73 | if sheet_name in default_sheetnames: 74 | default_sheetnames.remove(sheet_name) 75 | 76 | # 覆盖 77 | if not sheet_name in workbook.sheetnames: 78 | workbook.create_sheet(sheet_name) 79 | sheet = workbook[sheet_name] 80 | 81 | if "sheet_format" in data[sheet_name]: 82 | sheet_format: dict = data[sheet_name]["sheet_format"] 83 | for k in sheet_format: 84 | setattr(sheet.sheet_format, k, sheet_format[k]) 85 | # TODO 其他表格属性 86 | 87 | # 数据行 88 | sheet_data: list[list[dict[str, None]]] = data[sheet_name]["data"] 89 | for row in range(len(sheet_data)): 90 | for column in range(len(sheet_data[row])): 91 | cell_data: dict[str, None] = sheet_data[row][column] 92 | sheet.cell(row=row + 1, column=column + 1, value=cell_data["value"]) 93 | cell = sheet.cell(row=row + 1, column=column + 1) 94 | if "fill" in cell_data: 95 | _set_cell_fill(cell, cell_data["fill"]) 96 | if "border" in cell_data: 97 | _set_cell_border(cell, cell_data["border"]) 98 | # TODO: 其他单元格属性(先检查是否有对应属性) 99 | 100 | # 清除右侧的多余格 101 | for column in range(len(sheet_data[row]), sheet.max_column): 102 | cell = sheet.cell(row=row + 1, column=column + 1) 103 | if not cell.value is None: 104 | cell.value = "" 105 | 106 | for sheet_name in default_sheetnames: 107 | if sheet_name in workbook: 108 | del workbook[sheet_name] 109 | 110 | workbook.save(output_xlsx_file_path) 111 | workbook.close() 112 | 113 | 114 | def _to_dict(obj: None) -> dict: 115 | if not hasattr(obj, "__dict__"): 116 | return obj 117 | ret = {} 118 | for k in vars(obj): 119 | if k.startswith("_"): 120 | continue 121 | v = getattr(obj, k) 122 | if hasattr(v, "__dict__"): 123 | v = _to_dict(v) 124 | ret[k] = v 125 | return ret 126 | 127 | 128 | def _set_cell_fill(cell: cell.Cell, dict: dict) -> None: 129 | import openpyxl.styles.fills as fills 130 | 131 | PatternFill_attrs = ["patternType", "fgColor", "bgColor"] 132 | GradientFill_attrs = ["type", "degree", "left", "right", "top", "bottom", "stop"] 133 | 134 | is_meet = lambda attrs: len([attr for attr in attrs if attr in dict]) == len(attrs) 135 | 136 | if is_meet(PatternFill_attrs): 137 | cell.fill = fills.PatternFill( 138 | patternType=dict["patternType"], 139 | fgColor=_get_color_from_dict(dict["fgColor"]), 140 | bgColor=_get_color_from_dict(dict["bgColor"]), 141 | ) 142 | elif is_meet(GradientFill_attrs): 143 | sl = [] 144 | for dict in dict["stop"]: 145 | stop = fills.Stop(color=_get_color_from_dict(dict=["color"]), position=dict["position"]) 146 | sl.append(stop) 147 | cell.fill = fills.GradientFill( 148 | type=dict["type"], 149 | degree=dict["degree"], 150 | left=dict["left"], 151 | right=dict["right"], 152 | top=dict["top"], 153 | bottom=dict["bottom"], 154 | stop=sl, 155 | ) 156 | else: 157 | logging("Parse error: is not a valid dict.", dict) 158 | 159 | 160 | def _set_cell_border(cell: cell.Cell, dict: dict) -> None: 161 | for k in ["start", "end", "left", "right", "top", "bottom", "diagonal", "vertical", "horizontal"]: 162 | if k not in dict: 163 | dict[k] = None 164 | else: 165 | side_dict = dict[k] 166 | if not "style" in side_dict: 167 | side_dict["style"] = None 168 | if not "color" in side_dict: 169 | side_dict["color"] = None 170 | dict[k] = Side(style=side_dict["style"], color=_get_color_from_dict(side_dict["color"])) 171 | 172 | for k in ["diagonalUp", "diagonalDown"]: 173 | if k not in dict: 174 | dict[k] = False 175 | 176 | if "outline" not in dict: 177 | dict["outline"] = True 178 | 179 | cell.border = Border( 180 | start=dict["start"], 181 | end=dict["end"], 182 | left=dict["left"], 183 | right=dict["right"], 184 | top=dict["top"], 185 | bottom=dict["bottom"], 186 | diagonal=dict["diagonal"], 187 | vertical=dict["vertical"], 188 | horizontal=dict["horizontal"], 189 | diagonalUp=dict["diagonalUp"], 190 | diagonalDown=dict["diagonalDown"], 191 | outline=dict["outline"], 192 | ) 193 | 194 | 195 | def _get_color_from_dict(dict: dict) -> Color: 196 | if "index" in dict: 197 | dict["indexed"] = dict["index"] 198 | if not "tint" in dict: 199 | dict["tint"] = 0.0 200 | if "indexed" in dict: 201 | return Color(indexed=dict["indexed"], tint=dict["tint"]) 202 | elif "theme" in dict: 203 | return Color(theme=dict["theme"], tint=dict["tint"]) 204 | elif "auto" in dict: 205 | return Color(auto=dict["auto"]) 206 | else: 207 | return Color(rgb=dict["rgb"]) 208 | 209 | 210 | def main(argv): 211 | for arg in argv: 212 | if arg in ["-h", "--help"]: 213 | print("Valid command:") 214 | print("\tpython xlsx_json.py --dump_json path/to/excel.xlsx path/to/output.json") 215 | print("\tpython xlsx_json.py --override_xlsx path/to/data.json path/to/output.xlsx") 216 | exit(0) 217 | 218 | invalid_hint = "Invalid arguments for 'xlsx_json' tool. Please type '-h' or '--help' for more details." 219 | if len(argv) != 3: 220 | 221 | logging.error(invalid_hint) 222 | exit(1) 223 | 224 | if argv[0] == "--dump_json": 225 | dump_xlsx_to_json(argv[1], argv[2]) 226 | elif argv[0] == "--override_xlsx": 227 | override_xlsx_worksheets_from_json(argv[1], argv[2]) 228 | else: 229 | 230 | logging.error(invalid_hint) 231 | exit(1) 232 | 233 | 234 | if __name__ == "__main__": 235 | main(sys.argv[1:]) 236 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/table_tools/csv.gd: -------------------------------------------------------------------------------- 1 | """csv 表格工具 2 | 可选参数: 3 | - arr_dict_with_brackets=true/false 生成表格时是否为所有的数组与字典类型将加上方/花括号, 默认为 false 4 | """ 5 | @tool 6 | extends "table_tool.gd" 7 | 8 | const CSV_DELIM = "," 9 | 10 | var _last_parse_error: Error = ERR_PARSE_ERROR 11 | var _header: _TableHeader 12 | var _data: Array[Dictionary] = [] 13 | 14 | var arr_dict_with_brackets := false 15 | 16 | 17 | func _get_support_types() -> PackedByteArray: 18 | return [ 19 | TYPE_BOOL, 20 | TYPE_INT, 21 | TYPE_FLOAT, 22 | TYPE_STRING, 23 | TYPE_STRING_NAME, 24 | TYPE_NODE_PATH, 25 | TYPE_ARRAY, 26 | TYPE_DICTIONARY, 27 | TYPE_PACKED_BYTE_ARRAY, 28 | TYPE_PACKED_INT32_ARRAY, 29 | TYPE_PACKED_INT64_ARRAY, 30 | TYPE_PACKED_FLOAT32_ARRAY, 31 | TYPE_PACKED_FLOAT64_ARRAY, 32 | TYPE_PACKED_STRING_ARRAY, 33 | ] 34 | 35 | 36 | func _get_parse_error() -> Error: 37 | return _last_parse_error 38 | 39 | 40 | func _parse_table_file(csv_file: String, options: PackedStringArray) -> Error: 41 | var option_pairs := parse_options(options) 42 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false").to_lower() == "true" 43 | 44 | var fa := FileAccess.open(csv_file, FileAccess.READ) 45 | if not is_instance_valid(fa): 46 | _Log.error([_Localize.translate("无法读取csv文件: "), csv_file, " - ", error_string(FileAccess.get_open_error())]) 47 | _last_parse_error = FileAccess.get_open_error() 48 | return _last_parse_error 49 | 50 | _header = _TableHeader.new() 51 | var meta_list := fa.get_csv_line(CSV_DELIM) 52 | var descs := fa.get_csv_line(CSV_DELIM) 53 | var fields := fa.get_csv_line(CSV_DELIM) 54 | var types := fa.get_csv_line(CSV_DELIM) 55 | 56 | # 移除尾随空项(由其他软件产生) 57 | for i in range(meta_list.size() - 1, -1, -1): 58 | if meta_list[i].is_empty(): 59 | meta_list.remove_at(i) 60 | else: 61 | break 62 | 63 | for i in range(descs.size() - 1, -1, -1): 64 | if descs[i].is_empty(): 65 | descs.remove_at(i) 66 | else: 67 | break 68 | 69 | if meta_list.size() == 1 and meta_list[0] in ["PlaceHolder Meta List", "PlaceHolder Metas"]: # 兼容旧格式 70 | meta_list.clear() 71 | if descs.size() == 1 and descs[0] == "PlaceHolder Descriptions": 72 | descs.clear() 73 | 74 | _header.meta_list = meta_list 75 | _header.descriptions = descs 76 | _header.fields = fields 77 | _header.types = types 78 | 79 | # 检查字段与类型是否匹配 80 | if fields.size() != types.size(): 81 | _header = null 82 | _data.clear() 83 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("请使用生成工具创建合法的表头。")]) 84 | _last_parse_error = ERR_PARSE_ERROR 85 | return _last_parse_error 86 | 87 | # 检查字段名 88 | for f in fields: 89 | if not TextServerManager.get_primary_interface().is_valid_identifier(f) and not is_meta_filed(f): 90 | _header = null 91 | _data.clear() 92 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("非法标识符: "), f]) 93 | _last_parse_error = ERR_PARSE_ERROR 94 | return _last_parse_error 95 | # 检查类型 96 | for t in types: 97 | if get_type_id(t) <= 0: 98 | _header = null 99 | _data.clear() 100 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("不支持的类型: "), t]) 101 | _last_parse_error = ERR_PARSE_ERROR 102 | return _last_parse_error 103 | 104 | _data.clear() 105 | # 读取数据行 106 | while not fa.eof_reached(): 107 | var row := fa.get_csv_line(CSV_DELIM) 108 | if _is_empty_csv_row(row, fields.size()): 109 | # 跳过空行 110 | continue 111 | var row_data := {} 112 | for i in range(min(types.size(), row.size())): 113 | var type := get_type_id(types[i].strip_edges()) 114 | var field := fields[i].strip_edges() 115 | if type < 0: 116 | _header = null 117 | _data.clear() 118 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file]) 119 | _last_parse_error = ERR_PARSE_ERROR 120 | return _last_parse_error 121 | 122 | if row[i].is_empty(): 123 | # 跳过空数据,避免在此生成默认值 124 | continue 125 | 126 | var value: Variant = parse_value(row[i], type) 127 | 128 | if typeof(value) == TYPE_NIL: 129 | _header = null 130 | _data.clear() 131 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file]) 132 | _last_parse_error = ERR_PARSE_ERROR 133 | return _last_parse_error 134 | 135 | row_data[field] = value 136 | 137 | _data.push_back(row_data) 138 | 139 | return OK 140 | 141 | 142 | func _get_table_file_extension() -> String: 143 | return "csv" 144 | 145 | 146 | func _generate_table_file(save_path: String, header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error: 147 | var option_pairs := parse_options(options) 148 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false").to_lower() == "true" 149 | 150 | if not is_instance_valid(header): 151 | _Log.error([_Localize.translate("生成表格失败: "), error_string(ERR_INVALID_PARAMETER)]) 152 | return ERR_INVALID_PARAMETER 153 | 154 | # 生成用于跳过导入的.import 155 | var f := FileAccess.open(save_path + ".import", FileAccess.WRITE) 156 | if not is_instance_valid(f): 157 | _Log.error([_Localize.translate("生成表格失败,无法生成:"), save_path + ".import", " - ", error_string(FileAccess.get_open_error())]) 158 | return FAILED 159 | 160 | var engine_version := Engine.get_version_info() 161 | if engine_version.major >= 4 and engine_version.minor >= 3: 162 | # 4.3 之后使用skip 163 | f.store_string('[remap]\n\nimporter="skip"\n') 164 | else: 165 | # 4.3 之前使用keep 166 | f.store_string('[remap]\n\nimporter="keep"\n') 167 | 168 | f.close() 169 | 170 | var fa := FileAccess.open(save_path, FileAccess.WRITE) 171 | if not is_instance_valid(fa): 172 | _Log.error([_Localize.translate("生成表格失败: "), error_string(FileAccess.get_open_error())]) 173 | return FileAccess.get_open_error() 174 | 175 | # 确保非空行 176 | var meta_list := header.meta_list.duplicate() 177 | var descs := header.descriptions.duplicate() 178 | if meta_list.size() == 0: 179 | meta_list.push_back("PlaceHolder Meta List") 180 | if descs.size() <= 0: 181 | descs.push_back("PlaceHolder Descriptions") 182 | fa.store_csv_line(meta_list, CSV_DELIM) 183 | fa.store_csv_line(descs, CSV_DELIM) 184 | fa.store_csv_line(header.fields, CSV_DELIM) 185 | fa.store_csv_line(header.types, CSV_DELIM) 186 | for row in data_rows: 187 | fa.store_csv_line(row, CSV_DELIM) 188 | fa.close() 189 | 190 | return OK 191 | 192 | 193 | func _to_value_text(value: Variant) -> String: 194 | if not typeof(value) in get_support_types(): 195 | _Log.error([_Localize.translate("转换为文本失败,不支持的类型: "), value, " - ", type_string(typeof(value))]) 196 | return "" 197 | if typeof(value) in [TYPE_STRING, TYPE_NODE_PATH, TYPE_STRING_NAME]: 198 | return str(value) 199 | if typeof(value) > TYPE_ARRAY: 200 | # 转为数组给json 201 | value = type_convert(value, TYPE_ARRAY) 202 | # 不带两侧括号 203 | const fake_indent = "F`A@K*E&I#N|D-E+N/T" 204 | match typeof(value): 205 | TYPE_ARRAY: 206 | var text := JSON.stringify(value, fake_indent).replace("\n" + fake_indent, " ").trim_prefix("[ ").trim_suffix("\n]") 207 | if value.is_empty(): 208 | text = "" 209 | if arr_dict_with_brackets: 210 | text = "[%s]" % text 211 | return text 212 | TYPE_DICTIONARY: 213 | var text := JSON.stringify(value, fake_indent).replace("\n" + fake_indent, " ").trim_prefix("{ ").trim_suffix("\n}") 214 | if value.is_empty(): 215 | text = "" 216 | if arr_dict_with_brackets: 217 | text = "{%s}" % text 218 | return text 219 | _: 220 | return JSON.stringify(value) 221 | 222 | 223 | func _parse_value(text: String, type_id: int) -> Variant: 224 | if not type_id in get_support_types(): 225 | _Log.error([_Localize.translate("不支持的类型: "), type_string(type_id)]) 226 | return null 227 | 228 | var default := text.is_empty() 229 | if default: 230 | _Log.error([_Localize.translate("Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。")]) 231 | return null 232 | 233 | match type_id: 234 | TYPE_BOOL: 235 | return false if default else ("t" in text.to_lower()) 236 | TYPE_INT: 237 | return 0 if default else text.to_int() 238 | TYPE_FLOAT: 239 | return 0.0 if default else text.to_float() 240 | TYPE_STRING: 241 | return "" if default else text 242 | TYPE_STRING_NAME: 243 | return &"" if default else StringName(text) 244 | TYPE_NODE_PATH: 245 | return ^"" if default else NodePath(text) 246 | TYPE_ARRAY, TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY, TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY, TYPE_PACKED_STRING_ARRAY: 247 | if default: 248 | return convert([], type_id) 249 | var value_text := text 250 | if not text.begins_with("[") and not text.ends_with("]"): 251 | value_text = "[%s]" % text 252 | var arr: Variant = JSON.parse_string(value_text) 253 | if typeof(arr) != TYPE_ARRAY: 254 | _Log.error([_Localize.translate("非法值文本: "), text]) 255 | return null 256 | return convert(arr, type_id) 257 | TYPE_DICTIONARY: 258 | if default: 259 | return {} 260 | var value_text := text 261 | if not text.begins_with("{") and not text.ends_with("}"): 262 | value_text = "{%s}" % text 263 | var dict: Variant = JSON.parse_string(value_text) 264 | if typeof(dict) != TYPE_DICTIONARY: 265 | _Log.error([_Localize.translate("非法值文本: "), text]) 266 | return null 267 | return dict 268 | return null 269 | 270 | 271 | func _get_header() -> _TableHeader: 272 | return _header 273 | 274 | 275 | func _get_data() -> Array[Dictionary]: 276 | return _data 277 | 278 | 279 | func _get_tooltip_text() -> String: 280 | return """csv 表格工具 281 | 可选参数: 282 | - arr_dict_with_brackets=true/false 生成表格时是否为所有的数组与字典类型将加上方/花括号, 默认为 false 283 | """ 284 | 285 | 286 | # -------------- 287 | func _is_empty_csv_row(row: PackedStringArray, fields_count: int) -> bool: 288 | for i in range(min(row.size(), fields_count)): 289 | if not row[i].strip_edges().is_empty(): 290 | return false 291 | return true 292 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd: -------------------------------------------------------------------------------- 1 | ## 字典形式的 GDScript 导入,适合表格条目较多的情况 2 | ## Required Options: 3 | ## key=prop_name - 必选,指定用于查找的数据类属性,该属性的值在所有数据中不能重复,也不能留空 4 | ## Options: 5 | ## generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名 6 | ## pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true 7 | @tool 8 | extends "gdscript_default.gd" 9 | 10 | 11 | func _import( 12 | import_path: String, 13 | table_name: String, 14 | header: _TableHeader, 15 | data_class_name: String, 16 | data_class_script: String, 17 | instantiation: String, 18 | custom_setters: Dictionary, 19 | data_rows: Array[Dictionary], 20 | options: PackedStringArray 21 | ) -> Error: 22 | var option_pairs := parse_options(options) 23 | 24 | var priority_key := option_pairs.get("key", "") as String 25 | if priority_key.is_empty(): 26 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。")]) 27 | return ERR_INVALID_PARAMETER 28 | 29 | var can_use_typed_dictionary := false 30 | var version_info := Engine.get_version_info() 31 | if version_info.major > 4 or (version_info.major == 4 and version_info.minor >= 4): # 4.4 later 32 | can_use_typed_dictionary = true 33 | 34 | var fa := FileAccess.open(import_path, FileAccess.WRITE) 35 | if not is_instance_valid(fa): 36 | _Log.error([_Localize.translate("导表失败: "), error_string(FileAccess.get_open_error())]) 37 | return FileAccess.get_open_error() 38 | 39 | var pure_static := (option_pairs.get("pure_static", "true") as String).to_lower() == "true" 40 | var member_prefix := "static " if pure_static else "" 41 | if pure_static: 42 | fa.store_line("@static_unload") 43 | 44 | if TextServerManager.get_primary_interface().is_valid_identifier(table_name) and option_pairs.has("generate_class_name"): 45 | fa.store_line("class_name %s" % table_name) 46 | fa.store_line("") 47 | 48 | # TABLE_META_LIST 49 | var meta_list_text := "" 50 | for m in header.meta_list: 51 | if not meta_list_text.is_empty(): 52 | meta_list_text += ", " 53 | meta_list_text += '"%s"' % m 54 | fa.store_line("const TABLE_META_LIST: PackedStringArray = [%s]" % meta_list_text) 55 | fa.store_line("") 56 | 57 | # DataClass 58 | var property_list: Array[Dictionary] 59 | var property_default_values := {} 60 | var data_class := "" 61 | if not data_class_script.is_empty(): 62 | var internal_class := "" if data_class_name.is_empty() else ".%s" % data_class_name 63 | fa.store_line('const DataClass = preload("%s")%s' % [data_class_script, internal_class]) 64 | fa.store_line("") 65 | data_class = "DataClass" 66 | 67 | # 获取属性列表 68 | var script := ResourceLoader.load(data_class_script, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script 69 | if not data_class_name.is_empty(): 70 | var internal := data_class_name.split(".", false) 71 | while not internal.is_empty(): 72 | script = script.get_script_constant_map()[internal[0]] 73 | internal.remove_at(0) 74 | var base := script 75 | while true: 76 | property_list.append_array(base.get_script_property_list()) 77 | if not is_instance_valid(base.get_base_script()): 78 | property_list.append_array(ClassDB.class_get_property_list(base.get_instance_base_type())) 79 | var tmp_obj := ClassDB.instantiate(base.get_instance_base_type()) as Object 80 | for p in property_list: 81 | var n := p["name"] as String 82 | if n in tmp_obj: 83 | property_default_values[n] = tmp_obj.get(n) 84 | break 85 | base = base.get_base_script() 86 | 87 | for p in property_list: 88 | var n := p["name"] as String 89 | if n in property_default_values: 90 | continue 91 | property_default_values[n] = script.get_property_default_value(n) 92 | else: 93 | data_class = data_class_name 94 | property_list = ClassDB.class_get_property_list(data_class_name) 95 | var tmp_obj := ClassDB.instantiate(data_class_name) as Object 96 | for p in property_list: 97 | var n := p["name"] as String 98 | if n in tmp_obj: 99 | property_default_values[n] = tmp_obj.get(n) 100 | 101 | var existed_values := [] 102 | for d in data_rows: 103 | var v: Variant = d.get(priority_key, null) 104 | if typeof(v) == TYPE_NIL: 105 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("存在未配置key (%s)的数据: %s。") % [priority_key, d]]) 106 | return ERR_INVALID_PARAMETER 107 | if existed_values.has(v): 108 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("存在key (%s)重复的数据,重复的值为: %s。") % [priority_key, v]]) 109 | return ERR_INVALID_PARAMETER 110 | existed_values.push_back(v) 111 | 112 | var fields := header.fields.duplicate() 113 | var types := Array(header.types).map(to_type_id) as PackedByteArray 114 | 115 | if not fields.has(priority_key): 116 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。") % [priority_key]]) 117 | return ERR_INVALID_PARAMETER 118 | 119 | var priority_key_type_id: int = types[fields.find(priority_key)] 120 | 121 | var dictionary_type_text := "Dictionary" 122 | if can_use_typed_dictionary: 123 | dictionary_type_text = "Dictionary[%s, %s]" % [type_string(priority_key_type_id), data_class] 124 | 125 | # get_data 126 | fa.store_line(member_prefix + "func get_data() -> %s:" % dictionary_type_text) 127 | fa.store_line("\treturn _data") 128 | fa.store_line("") 129 | fa.store_line("") 130 | 131 | # get_record 132 | fa.store_line(member_prefix + "func get_record(key: %s) -> %s:" % [type_string(priority_key_type_id), data_class]) 133 | fa.store_line("\treturn _data.get(key, null)") 134 | fa.store_line("") 135 | fa.store_line("") 136 | 137 | # find_by_property 138 | fa.store_line(member_prefix + "func find_by_property(prop_name: StringName, target_value: Variant) -> %s:" % data_class) 139 | fa.store_line("\tfor d: %s in _data.values():" % data_class) 140 | fa.store_line("\t\tif d.get(prop_name) == target_value:") 141 | fa.store_line("\t\t\treturn d") 142 | fa.store_line("\treturn null") 143 | fa.store_line("") 144 | fa.store_line("") 145 | 146 | # find_by_getter 147 | fa.store_line(member_prefix + "func find_by_getter(getter_name: StringName, target_value: Variant) -> %s:" % data_class) 148 | fa.store_line("\tfor d: %s in _data.values():" % data_class) 149 | fa.store_line("\t\tif d.call(getter_name) == target_value:") 150 | fa.store_line("\t\t\treturn d") 151 | fa.store_line("\treturn null") 152 | fa.store_line("") 153 | fa.store_line("") 154 | 155 | # find 156 | fa.store_line(member_prefix + "func find(indicate: Callable) -> %s:" % data_class) 157 | fa.store_line("\tfor d: %s in _data.values():" % data_class) 158 | fa.store_line("\t\tif indicate.call(d):") 159 | fa.store_line("\t\t\treturn d") 160 | fa.store_line("\treturn null") 161 | fa.store_line("") 162 | fa.store_line("") 163 | 164 | # filter 165 | fa.store_line(member_prefix + "func filter(indicate: Callable) -> Array[%s]:" % data_class) 166 | if data_class == "DataClass": 167 | fa.store_line("\treturn Array(_data.values().filter(indicate), TYPE_OBJECT, (DataClass as Script).get_instance_base_type(), DataClass)") 168 | else: 169 | fa.store_line("\treturn Array(_data.values().filter(indicate), TYPE_OBJECT, &\"%s\", null)" % data_class) 170 | fa.store_line("") 171 | fa.store_line("") 172 | 173 | fa.store_line("# -----------------------------------------------------------------------") 174 | # 过滤meta字段 175 | var idx := 0 176 | while idx < fields.size(): 177 | if is_meta_filed(fields[idx]): 178 | fields.remove_at(idx) 179 | types.remove_at(idx) 180 | continue 181 | idx += 1 182 | 183 | # _make_data 184 | instantiation = instantiation.strip_edges() 185 | var args := ( 186 | ( 187 | Array(instantiation.split("(", false, 1)[1].split(")", false, 1)[0].split(",")).map(func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}")) 188 | as PackedStringArray 189 | ) 190 | if not instantiation.ends_with("()") 191 | else PackedStringArray() 192 | ) 193 | var fields_with_type := fields.duplicate() 194 | for i in range(fields_with_type.size()): 195 | fields_with_type[i] = "%s: %s" % [fields_with_type[i], type_string(types[i])] 196 | fa.store_line(member_prefix + "func _make_data(%s) -> %s:" % [", ".join(fields_with_type), data_class]) 197 | fa.store_line("\tvar ret := %s.%s(%s)" % [data_class, instantiation.split("(")[0], ", ".join(args)]) 198 | 199 | var valid_properties :Dictionary = {} 200 | for prop in property_list: 201 | valid_properties[prop["name"]] = prop 202 | 203 | var hinted_fields: PackedStringArray = [] 204 | for f: String in Array(fields).filter(func(f: String) -> bool: return not args.has(f)): 205 | if custom_setters.has(f): 206 | fa.store_line("\tret.%s(%s)" % [custom_setters[f], f]) 207 | elif valid_properties.has(f): 208 | var prop := valid_properties[f] as Dictionary 209 | if prop.type == TYPE_ARRAY: 210 | fa.store_line("\tret.%s.assign(%s)" % [f, f]) 211 | elif prop.type == TYPE_DICTIONARY and (Engine.get_version_info().major > 4 or Engine.get_version_info().minor >= 4): 212 | # 4.4 以上支持类型化字典 213 | fa.store_line("\tret.%s.assign(%s)" % [f, f]) 214 | else: 215 | fa.store_line("\tret.%s = %s" % [f, f]) 216 | elif not hinted_fields.has(f): 217 | # 只提示一次 218 | _Log.warning([_Localize.translate("无法被赋值的字段将被跳过: "), f]) 219 | hinted_fields.push_back(f) 220 | fa.store_line("\treturn ret") 221 | fa.store_line("") 222 | fa.store_line("") 223 | 224 | # 数据行 225 | fa.store_line(member_prefix + "var _data: %s = {}" % dictionary_type_text) 226 | fa.store_line("") 227 | fa.store_line("") 228 | 229 | if member_prefix.is_empty(): 230 | fa.store_line(member_prefix + "func _init() -> void:") 231 | else: 232 | fa.store_line(member_prefix + "func _static_init() -> void:") 233 | for row in data_rows: 234 | var args_text_list := PackedStringArray() 235 | for i in range(fields.size()): 236 | var f := fields[i] 237 | var t := types[i] 238 | args_text_list.push_back(_get_value_text(row, f, t, property_default_values)) 239 | var priority_key_text := _get_value_text(row, priority_key, priority_key_type_id, property_default_values) 240 | fa.store_line("\t_data[%s] = _make_data(%s)" % [priority_key_text, ", ".join(args_text_list)]) 241 | fa.store_line("\t_data.make_read_only()") 242 | if data_rows.is_empty(): 243 | fa.store_line("\tpass") 244 | fa.store_line("") 245 | fa.close() 246 | 247 | return OK 248 | 249 | 250 | func _get_tooltip_text() -> String: 251 | return """字典形式的 GDScript 导入,适合表格条目较多的情况 252 | 必选参数: 253 | - key=prop_name - 必选,指定用于查找的数据类属性,该属性的值在所有数据中不能重复,也不能留空 254 | 可选参数: 255 | - generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名 256 | - pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true 257 | """ 258 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd: -------------------------------------------------------------------------------- 1 | """Excel(xlsx) 表格工具 2 | 必要参数: 3 | - sheet=your_sheet_name 指定要解析的工作表,如果xlsx中存在多个工作表,则该参数必须指定。 4 | 可选参数: 5 | - parse_sheet_must_exists=true/false 为true时如果指定工作表不存在时将发生解析错误。默认 false 6 | - arr_dict_with_brackets=true/false 为true时生成表格时所有的数组与字典类型将加上方/花括号。默认为 false 7 | - colorize_header=true 是否对生成的表头单元格被赋予颜色。默认 true 8 | """ 9 | @tool 10 | extends "csv.gd" 11 | 12 | const META_COLOR = Color.DARK_GRAY 13 | const DESC_COLOR = Color.AQUA 14 | const FIELD_COLOR = Color.DARK_SALMON 15 | const TYPE_COLOR = Color.LAWN_GREEN 16 | const META_FILED_COLOR = Color.ALICE_BLUE 17 | 18 | const _PY_TOOL_RELATIVE_PATH = "py/xlsx_json.py" 19 | 20 | var _py_tool_path: String = ProjectSettings.globalize_path((get_script().resource_path as String).get_base_dir().path_join(_PY_TOOL_RELATIVE_PATH)) 21 | var _tmp_json_path: String = ProjectSettings.globalize_path(EditorInterface.get_editor_paths().get_project_settings_dir().path_join("_xlsx_tmp_.json")) 22 | 23 | 24 | func _parse_table_file(xlsx_file: String, options: PackedStringArray) -> Error: 25 | var sheet_name: String = "" 26 | var parse_sheet_must_exists := false 27 | 28 | var option_pairs := parse_options(options) 29 | sheet_name = option_pairs.get("sheet", "") 30 | parse_sheet_must_exists = option_pairs.get("parse_sheet_must_exists", "false") == "true" 31 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false") == "true" 32 | 33 | if sheet_name.is_empty(): 34 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("必须使用 sheet=your_sheet_name 选项指定工作表。")]) 35 | _last_parse_error = ERR_INVALID_PARAMETER 36 | return _last_parse_error 37 | 38 | if not FileAccess.file_exists(xlsx_file): 39 | _Log.error([_Localize.translate("无法读取xlsx文件: "), xlsx_file, " - ", error_string(ERR_FILE_NOT_FOUND)]) 40 | _last_parse_error = ERR_FILE_NOT_FOUND 41 | return _last_parse_error 42 | 43 | var output := [] 44 | var err := OS.execute("python", [_py_tool_path, "--dump_json", ProjectSettings.globalize_path(xlsx_file), _tmp_json_path], output, true) 45 | if err != OK: 46 | _Log.error([_Localize.translate("无法解析xlsx文件: "), xlsx_file, " - ", "\n".join(output)]) 47 | _last_parse_error = FAILED 48 | return _last_parse_error 49 | 50 | var json := FileAccess.get_file_as_string(_tmp_json_path) 51 | var sheets: Dictionary = JSON.parse_string(json) as Dictionary 52 | 53 | if not sheet_name in sheets: 54 | if parse_sheet_must_exists: 55 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("不存在指定的工作表: "), sheet_name]) 56 | _last_parse_error = ERR_PARSE_ERROR 57 | return _last_parse_error 58 | else: 59 | _last_parse_error = OK 60 | return _last_parse_error 61 | 62 | var sheet := Array(sheets[sheet_name]["data"], TYPE_ARRAY, &"", null) as Array[Array] 63 | if sheet.size() < 4: 64 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("非法格式")]) 65 | _last_parse_error = ERR_PARSE_ERROR 66 | return _last_parse_error 67 | 68 | _header = _TableHeader.new() 69 | var meta_list := _to_str_arr(sheet[0]) 70 | var descs := _to_str_arr(sheet[1]) 71 | var fields := _to_str_arr(sheet[2]) 72 | var types := _to_str_arr(sheet[3]) 73 | 74 | # 移除尾随空项(由其他软件产生) 75 | for i in range(meta_list.size() - 1, -1, -1): 76 | if meta_list[i].is_empty(): 77 | meta_list.remove_at(i) 78 | else: 79 | break 80 | 81 | for i in range(descs.size() - 1, -1, -1): 82 | if descs[i].is_empty(): 83 | descs.remove_at(i) 84 | else: 85 | break 86 | 87 | if meta_list.size() == 1 and meta_list[0] in ["PlaceHolder Meta List", "PlaceHolder Metas"]: # 兼容旧格式 88 | meta_list.clear() 89 | if descs.size() == 1 and descs[0] == "PlaceHolder Descriptions": 90 | descs.clear() 91 | 92 | _header.meta_list = meta_list 93 | _header.descriptions = descs 94 | _header.fields = fields 95 | _header.types = types 96 | 97 | # 检查字段与类型是否匹配 98 | if fields.size() != types.size(): 99 | _header = null 100 | _data.clear() 101 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("请使用生成工具创建合法的表头。")]) 102 | _last_parse_error = ERR_PARSE_ERROR 103 | return _last_parse_error 104 | 105 | # 检查字段名 106 | for f in fields: 107 | if not TextServerManager.get_primary_interface().is_valid_identifier(f) and not is_meta_filed(f): 108 | _header = null 109 | _data.clear() 110 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("非法标识符: "), f]) 111 | _last_parse_error = ERR_PARSE_ERROR 112 | return _last_parse_error 113 | # 检查类型 114 | for t in types: 115 | if get_type_id(t) <= 0: 116 | _header = null 117 | _data.clear() 118 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("不支持的类型: "), t]) 119 | _last_parse_error = ERR_PARSE_ERROR 120 | return _last_parse_error 121 | 122 | _data.clear() 123 | # 读取数据行 124 | for row_idx in range(4, sheet.size()): 125 | var row := _to_str_arr(sheet[row_idx]) 126 | if _is_empty_csv_row(row, fields.size()): 127 | # 跳过空行 128 | continue 129 | var row_data := {} 130 | for i in range(min(types.size(), row.size())): 131 | var type := get_type_id(types[i].strip_edges()) 132 | var field := fields[i].strip_edges() 133 | if type < 0: 134 | _header = null 135 | _data.clear() 136 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file]) 137 | _last_parse_error = ERR_PARSE_ERROR 138 | return _last_parse_error 139 | 140 | if row[i].is_empty(): 141 | # 跳过空数据,避免在此生成默认值 142 | continue 143 | 144 | var value: Variant = parse_value(row[i], type) 145 | 146 | if typeof(value) == TYPE_NIL: 147 | _header = null 148 | _data.clear() 149 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file]) 150 | _last_parse_error = ERR_PARSE_ERROR 151 | return _last_parse_error 152 | 153 | row_data[field] = value 154 | 155 | _data.push_back(row_data) 156 | 157 | return OK 158 | 159 | 160 | func _get_table_file_extension() -> String: 161 | return "xlsx" 162 | 163 | 164 | func _generate_table_file(save_path: String, header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error: 165 | save_path = ProjectSettings.globalize_path(save_path) 166 | var sheet_name: String = "" 167 | var colorize_header := true 168 | 169 | var option_pairs := parse_options(options) 170 | sheet_name = option_pairs.get("sheet", "") 171 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false") == "true" 172 | colorize_header = option_pairs.get("colorize_header", "true") == "true" 173 | 174 | if sheet_name.is_empty(): 175 | _Log.error([_Localize.translate("解析xlsx文件: "), save_path, " - ", _Localize.translate("必须使用 sheet=your_sheet_name 选项指定工作表。")]) 176 | _last_parse_error = ERR_INVALID_PARAMETER 177 | return _last_parse_error 178 | 179 | if not is_instance_valid(header): 180 | _Log.error([_Localize.translate("生成表格失败: "), error_string(ERR_INVALID_PARAMETER)]) 181 | return ERR_INVALID_PARAMETER 182 | 183 | var json_data := {} 184 | var sheet_data := [] 185 | json_data[sheet_name] = {"data": sheet_data} 186 | 187 | # 确保非空行 188 | var meta_list := header.meta_list.duplicate() 189 | var descs := header.descriptions.duplicate() 190 | if meta_list.size() == 0: 191 | meta_list.push_back("PlaceHolder Meta List") 192 | if descs.size() <= 0: 193 | descs.push_back("PlaceHolder Descriptions") 194 | 195 | sheet_data.push_back(_to_row(meta_list)) 196 | sheet_data.push_back(_to_row(descs)) 197 | sheet_data.push_back(_to_row(header.fields)) 198 | sheet_data.push_back(_to_row(header.types)) 199 | 200 | if colorize_header: 201 | _colorize_header(sheet_data) 202 | 203 | for row in data_rows: 204 | var row_data := [] 205 | row_data.resize(row.size()) 206 | for i in range(row.size()): 207 | if row[i].is_empty(): 208 | row_data[i] = "" 209 | else: 210 | var type_id := get_type_id(header.types[i]) 211 | if type_id in [TYPE_INT, TYPE_FLOAT, TYPE_BOOL]: 212 | row_data[i] = parse_value(row[i], type_id) 213 | elif ( 214 | not arr_dict_with_brackets 215 | and type_id in [TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY, TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY] 216 | ): 217 | var converted: Variant = parse_value(row[i], type_id) 218 | if converted.size() == 1: 219 | row_data[i] = converted[0] 220 | else: 221 | row_data[i] = row[i] 222 | elif not arr_dict_with_brackets and type_id == TYPE_ARRAY: 223 | var converted: Variant = parse_value(row[i], type_id) 224 | if converted.size() == 1 and typeof(converted[0]) in [TYPE_INT, TYPE_FLOAT, TYPE_BOOL]: 225 | row_data[i] = converted[0] 226 | else: 227 | row_data[i] = row[i] 228 | else: 229 | row_data[i] = row[i] 230 | sheet_data.push_back(_to_row(row_data)) 231 | 232 | var fa := FileAccess.open(_tmp_json_path, FileAccess.WRITE) 233 | fa.store_string(JSON.stringify(json_data)) 234 | fa.close() 235 | 236 | var output := [] 237 | var err := OS.execute("python", [_py_tool_path, "--override_xlsx", _tmp_json_path, ProjectSettings.globalize_path(save_path)], output, true) 238 | if err != OK: 239 | _Log.error([_Localize.translate("无法覆盖xlsx文件: "), save_path, " - ", "\n".join(output)]) 240 | _last_parse_error = FAILED 241 | return _last_parse_error 242 | 243 | return OK 244 | 245 | 246 | func _get_tooltip_text() -> String: 247 | return """Excel(xlsx) 表格工具 248 | 必要参数: 249 | - sheet=your_sheet_name 指定要解析的工作表,如果xlsx中存在多个工作表,则该参数必须指定。 250 | 可选参数: 251 | - parse_sheet_must_exists=true/false 为true时如果指定工作表不存在时将发生解析错误。默认 false 252 | - arr_dict_with_brackets=true/false 为true时生成表格时所有的数组与字典类型将加上方/花括号。默认为 false 253 | - colorize_header=true 是否对生成的表头单元格被赋予颜色。默认 true 254 | """ 255 | 256 | 257 | # ----------------- 258 | func _colorize_header(sheet_data: Array) -> void: 259 | assert(sheet_data.size() >= 4) 260 | # meta 261 | for cell: Dictionary in sheet_data[0]: 262 | cell["fill"] = _make_pattern_fill(META_COLOR) 263 | cell["border"] = _make_border() 264 | # desc 265 | for cell: Dictionary in sheet_data[1]: 266 | cell["fill"] = _make_pattern_fill(DESC_COLOR) 267 | cell["border"] = _make_border() 268 | # field & type 269 | for i in range(sheet_data[2].size()): 270 | if is_meta_filed(sheet_data[2][i]["value"]): 271 | sheet_data[2][i]["fill"] = _make_pattern_fill(META_FILED_COLOR) 272 | sheet_data[3][i]["fill"] = _make_pattern_fill(META_FILED_COLOR) 273 | if sheet_data[1].size() > i: 274 | sheet_data[1][i]["fill"] = _make_pattern_fill(META_FILED_COLOR) 275 | else: 276 | sheet_data[2][i]["fill"] = _make_pattern_fill(FIELD_COLOR) 277 | sheet_data[3][i]["fill"] = _make_pattern_fill(TYPE_COLOR) 278 | sheet_data[2][i]["border"] = _make_border() 279 | sheet_data[3][i]["border"] = _make_border() 280 | 281 | 282 | func _make_pattern_fill(fgColor: Color, bgColor: Color = Color(0.0, 0.0, 0.0, 0.0)) -> Dictionary: 283 | return { 284 | patternType = "solid", 285 | fgColor = {rgb = fgColor.to_html(false)}, 286 | bgColor = {rgb = bgColor.to_html(false)}, 287 | } 288 | 289 | 290 | func _make_border() -> Dictionary: 291 | return { 292 | left = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true}, 293 | right = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true}, 294 | top = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true}, 295 | bottom = {style = "thin", color = {rgb = Color.BLACK.to_html(false), outline = true}}, 296 | } 297 | 298 | 299 | func _strip_right_null_cell(row: Array) -> Array[Dictionary]: 300 | # row:Array[Dictionary] 301 | var row_data := Array(row.duplicate(), TYPE_DICTIONARY, &"", null) as Array[Dictionary] 302 | for i in range(row_data.size() - 1, -1, -1): 303 | if row_data[i]["value"] == null: 304 | row_data.pop_back() 305 | else: 306 | break 307 | return row_data 308 | 309 | 310 | func _to_str_arr(row: Array) -> PackedStringArray: 311 | # roe: Array[Dictionary] 312 | return _strip_right_null_cell(row).map(func(e: Dictionary) -> String: return "" if e["value"] == null else str(e["value"])) 313 | 314 | 315 | func _to_row(str_arr: Array) -> Array[Dictionary]: 316 | return Array(str_arr.map(func(e: Variant) -> Dictionary: return {value = e}), TYPE_DICTIONARY, &"", null) 317 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd: -------------------------------------------------------------------------------- 1 | ## 默认的 GDScript 导入 (TypedArray) 适合表格条目较少的情况。 2 | ## 可选配置参数: 3 | ## generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名 4 | ## pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true 5 | @tool 6 | extends "import_tool.gd" 7 | 8 | 9 | func _import( 10 | import_path: String, 11 | table_name: String, 12 | header: _TableHeader, 13 | data_class_name: String, 14 | data_class_script: String, 15 | instantiation: String, 16 | custom_setters: Dictionary, 17 | data_rows: Array[Dictionary], 18 | options: PackedStringArray 19 | ) -> Error: 20 | var fa := FileAccess.open(import_path, FileAccess.WRITE) 21 | if not is_instance_valid(fa): 22 | _Log.error([_Localize.translate("导表失败: "), error_string(FileAccess.get_open_error())]) 23 | return FileAccess.get_open_error() 24 | 25 | var option_pairs := parse_options(options) 26 | 27 | var pure_static := (option_pairs.get("pure_static", "true") as String).to_lower() == "true" 28 | var member_prefix := "static " if pure_static else "" 29 | 30 | if pure_static: 31 | fa.store_line("@static_unload") 32 | 33 | if TextServerManager.get_primary_interface().is_valid_identifier(table_name) and option_pairs.has("generate_class_name"): 34 | fa.store_line("class_name %s" % table_name) 35 | fa.store_line("") 36 | 37 | # TABLE_META_LIST 38 | var meta_list_text := "" 39 | for m in header.meta_list: 40 | if not meta_list_text.is_empty(): 41 | meta_list_text += ", " 42 | meta_list_text += '"%s"' % m 43 | fa.store_line("const TABLE_META_LIST: PackedStringArray = [%s]" % meta_list_text) 44 | fa.store_line("") 45 | 46 | # DataClass 47 | var property_list: Array[Dictionary] 48 | var property_default_values: Dictionary = {} 49 | var data_class := "" 50 | if not data_class_script.is_empty(): 51 | var internal_class := "" if data_class_name.is_empty() else ".%s" % data_class_name 52 | fa.store_line('const DataClass = preload("%s")%s' % [data_class_script, internal_class]) 53 | fa.store_line("") 54 | data_class = "DataClass" 55 | 56 | # 获取属性列表 57 | var script := ResourceLoader.load(data_class_script, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script 58 | if not data_class_name.is_empty(): 59 | var internal := data_class_name.split(".", false) 60 | while not internal.is_empty(): 61 | script = script.get_script_constant_map()[internal[0]] 62 | internal.remove_at(0) 63 | var base := script 64 | while true: 65 | property_list.append_array(base.get_script_property_list()) 66 | if not is_instance_valid(base.get_base_script()): 67 | property_list.append_array(ClassDB.class_get_property_list(base.get_instance_base_type())) 68 | var tmp_obj := ClassDB.instantiate(base.get_instance_base_type()) as Object 69 | for p in property_list: 70 | var n := p["name"] as String 71 | if n in tmp_obj: 72 | property_default_values[n] = tmp_obj.get(n) 73 | break 74 | base = base.get_base_script() 75 | 76 | for p in property_list: 77 | var n := p["name"] as String 78 | if n in property_default_values: 79 | continue 80 | property_default_values[n] = script.get_property_default_value(n) 81 | else: 82 | data_class = data_class_name 83 | property_list = ClassDB.class_get_property_list(data_class_name) 84 | var tmp_obj := ClassDB.instantiate(data_class_name) as Object 85 | for p in property_list: 86 | var n := p["name"] as String 87 | if n in tmp_obj: 88 | property_default_values[n] = tmp_obj.get(n) 89 | 90 | # get_data 91 | fa.store_line(member_prefix + "func get_data() -> Array[%s]:" % data_class) 92 | fa.store_line("\treturn _data") 93 | fa.store_line("") 94 | fa.store_line("") 95 | 96 | # find_by_property 97 | fa.store_line(member_prefix + "func find_by_property(prop_name: StringName, target_value: Variant) -> %s:" % data_class) 98 | fa.store_line("\tfor d in _data:") 99 | fa.store_line("\t\tif d.get(prop_name) == target_value:") 100 | fa.store_line("\t\t\treturn d") 101 | fa.store_line("\treturn null") 102 | fa.store_line("") 103 | fa.store_line("") 104 | 105 | # find_by_getter 106 | fa.store_line(member_prefix + "func find_by_getter(getter_name: StringName, target_value: Variant) -> %s:" % data_class) 107 | fa.store_line("\tfor d in _data:") 108 | fa.store_line("\t\tif d.call(getter_name) == target_value:") 109 | fa.store_line("\t\t\treturn d") 110 | fa.store_line("\treturn null") 111 | fa.store_line("") 112 | fa.store_line("") 113 | 114 | # find 115 | fa.store_line(member_prefix + "func find(indicate: Callable) -> %s:" % data_class) 116 | fa.store_line("\tfor d in _data:") 117 | fa.store_line("\t\tif indicate.call(d):") 118 | fa.store_line("\t\t\treturn d") 119 | fa.store_line("\treturn null") 120 | fa.store_line("") 121 | fa.store_line("") 122 | 123 | # filter 124 | fa.store_line(member_prefix + "func filter(indicate: Callable) -> Array[%s]:" % data_class) 125 | fa.store_line("\treturn _data.filter(indicate)") 126 | fa.store_line("") 127 | fa.store_line("") 128 | 129 | fa.store_line("# -----------------------------------------------------------------------") 130 | # 过滤meta字段 131 | var fields := header.fields.duplicate() 132 | var types := Array(header.types).map(to_type_id) as PackedByteArray 133 | var idx := 0 134 | while idx < fields.size(): 135 | if is_meta_filed(fields[idx]): 136 | fields.remove_at(idx) 137 | types.remove_at(idx) 138 | continue 139 | idx += 1 140 | 141 | # _make_data 142 | instantiation = instantiation.strip_edges() 143 | var args := ( 144 | ( 145 | Array(instantiation.split("(", false, 1)[1].split(")", false, 1)[0].split(",")).map(func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}")) 146 | as PackedStringArray 147 | ) 148 | if not instantiation.ends_with("()") 149 | else PackedStringArray() 150 | ) 151 | var fields_with_type := fields.duplicate() 152 | for i in range(fields_with_type.size()): 153 | fields_with_type[i] = "%s: %s" % [fields_with_type[i], type_string(types[i])] 154 | fa.store_line(member_prefix + "func _make_data(%s) -> %s:" % [", ".join(fields_with_type), data_class]) 155 | fa.store_line("\tvar ret := %s.%s(%s)" % [data_class, instantiation.split("(")[0], ", ".join(args)]) 156 | var hinted_fields: PackedStringArray = [] 157 | 158 | var valid_properties :Dictionary = {} 159 | for prop in property_list: 160 | valid_properties[prop["name"]] = prop 161 | 162 | for f: String in Array(fields).filter(func(f: String) -> bool: return not args.has(f)): 163 | if custom_setters.has(f): 164 | fa.store_line("\tret.%s(%s)" % [custom_setters[f], f]) 165 | elif valid_properties.has(f): 166 | var prop := valid_properties[f] as Dictionary 167 | if prop.type == TYPE_ARRAY: 168 | fa.store_line("\tret.%s.assign(%s)" % [f, f]) 169 | elif prop.type == TYPE_DICTIONARY and (Engine.get_version_info().major > 4 or Engine.get_version_info().minor >= 4): 170 | # 4.4 以上支持类型化字典 171 | fa.store_line("\tret.%s.assign(%s)" % [f, f]) 172 | else: 173 | fa.store_line("\tret.%s = %s" % [f, f]) 174 | elif not hinted_fields.has(f): 175 | # 只提示一次 176 | _Log.warning([_Localize.translate("无法被赋值的字段将被跳过: "), f]) 177 | hinted_fields.push_back(f) 178 | fa.store_line("\treturn ret") 179 | fa.store_line("") 180 | fa.store_line("") 181 | 182 | # 数据行 183 | fa.store_line(member_prefix + "var _data:Array[%s] = [" % data_class) 184 | for row in data_rows: 185 | var args_text_list := PackedStringArray() 186 | for i in range(fields.size()): 187 | var f := fields[i] 188 | var t := types[i] 189 | args_text_list.push_back(_get_value_text(row, f, t, property_default_values)) 190 | fa.store_line("\t_make_data(%s)," % [", ".join(args_text_list)]) 191 | fa.store_line("]") 192 | fa.store_line("") 193 | fa.store_line("") 194 | 195 | # 构造,使数据只读 196 | if member_prefix.is_empty(): 197 | fa.store_line(member_prefix + "func _init() -> void:") 198 | else: 199 | fa.store_line(member_prefix + "func _static_init() -> void:") 200 | fa.store_line("\t_data.make_read_only()") 201 | fa.store_line("") 202 | fa.close() 203 | 204 | return OK 205 | 206 | 207 | func _get_import_file_extension() -> String: 208 | return "gd" 209 | 210 | 211 | func _get_value_text(row: Dictionary, field: String, type_id: int, default_values: Dictionary) -> String: 212 | var value: Variant = row.get(field, default_values.get(field, null)) 213 | 214 | var default := typeof(value) == TYPE_NIL 215 | var converted_value: Variant = type_convert(value, type_id) 216 | return __get_value_text(converted_value, default) 217 | 218 | 219 | func __get_value_text(converted_value: Variant, default := false) -> String: 220 | var type_id: Variant = typeof(converted_value) 221 | var type := type_string(type_id) 222 | match type_id: 223 | TYPE_NIL: 224 | return "null" 225 | TYPE_BOOL: 226 | return str(false) if default else str(converted_value) 227 | TYPE_INT: 228 | return str(0) if default else str(converted_value) 229 | TYPE_FLOAT: 230 | return str(0.0) if default else str(converted_value) 231 | TYPE_STRING: 232 | return '""' if default else '"%s"' % converted_value 233 | TYPE_VECTOR2, TYPE_VECTOR2I: 234 | return (type + "()") if default else (type + "(%s, %s)" % [converted_value.x, converted_value.y]) 235 | TYPE_VECTOR3, TYPE_VECTOR3I: 236 | return (type + "()") if default else (type + "(%s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z]) 237 | TYPE_RECT2, TYPE_RECT2I: 238 | if default: 239 | return type + "()" 240 | else: 241 | return type + "(%s, %s, %s, %s)" % [converted_value.position.x, converted_value.position.y, converted_value.size.x, converted_value.size.y] 242 | TYPE_TRANSFORM2D: 243 | if default: 244 | return type + "()" 245 | else: 246 | var tran := converted_value as Transform2D 247 | return type + "(Vector2(%s, %s), Vector2(%s, %s), Vector2(%s, %s))" % [tran.x.x, tran.x.y, tran.y.x, tran.y.y, tran.origin.x, tran.origin.y] 248 | TYPE_VECTOR4, TYPE_VECTOR4I: 249 | if default: 250 | return type + "()" 251 | else: 252 | return type + "(%s, %s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z, converted_value.w] 253 | TYPE_PLANE: 254 | if default: 255 | return type + "()" 256 | else: 257 | var plane := converted_value as Plane 258 | return type + "(Vector3(%s, %s, %s), %s)" % [plane.normal.x, plane.normal.y, plane.normal.z, plane.d] 259 | TYPE_QUATERNION: 260 | if default: 261 | return type + "()" 262 | else: 263 | return type + "(%s, %s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z, converted_value.w] 264 | TYPE_AABB: 265 | if default: 266 | return type + "()" 267 | else: 268 | var aabb := converted_value as AABB 269 | return type + "(Vector3(%s, %s, %s), Vector3(%s, %s, %s))" % [aabb.position.x, aabb.position.y, aabb.position.z, aabb.size.x, aabb.size.y, aabb.size.z] 270 | TYPE_BASIS: 271 | if default: 272 | return type + "()" 273 | else: 274 | var basis := converted_value as Basis 275 | return ( 276 | type 277 | + ( 278 | "(Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s))" 279 | % [basis.x.x, basis.x.y, basis.x.z, basis.y.x, basis.y.y, basis.y.z, basis.z.x, basis.z.y, basis.z.z] 280 | ) 281 | ) 282 | TYPE_TRANSFORM3D: 283 | if default: 284 | return type + "()" 285 | else: 286 | var trans := converted_value as Transform3D 287 | var basis := trans.basis 288 | var origin := trans.origin 289 | return ( 290 | type 291 | + ( 292 | "(Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s))" 293 | % [basis.x.x, basis.x.y, basis.x.z, basis.y.x, basis.y.y, basis.y.z, basis.z.x, basis.z.y, basis.z.z, origin.x, origin.y, origin.z] 294 | ) 295 | ) 296 | TYPE_PROJECTION: 297 | if default: 298 | return type + "()" 299 | else: 300 | var proj := converted_value as Projection 301 | return ( 302 | type 303 | + ( 304 | "(Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s))" 305 | % [ 306 | proj.x.x, 307 | proj.x.y, 308 | proj.x.z, 309 | proj.x.w, 310 | proj.y.x, 311 | proj.y.y, 312 | proj.y.z, 313 | proj.y.w, 314 | proj.z.x, 315 | proj.z.y, 316 | proj.z.z, 317 | proj.z.w, 318 | proj.w.x, 319 | proj.w.y, 320 | proj.w.z, 321 | proj.w.w, 322 | ] 323 | ) 324 | ) 325 | TYPE_COLOR: 326 | if default: 327 | return type + "()" 328 | else: 329 | var color := converted_value as Color 330 | return type + "(%s, %s, %s, %s)" % [color.r, color.g, color.b, color.a] 331 | TYPE_STRING_NAME: 332 | return '&""' if default else '&"%s"' % String(converted_value) 333 | TYPE_NODE_PATH: 334 | return '^""' if default else '^"%s"' % String(converted_value) 335 | TYPE_DICTIONARY: 336 | var elements: PackedStringArray = [] 337 | for k: Variant in converted_value: 338 | var v: Variant = converted_value[k] 339 | elements.push_back("%s: %s" % [__get_value_text(k), __get_value_text(v)]) 340 | return "{}" if default else "{%s}" % ", ".join(elements) 341 | _: 342 | if type_id >= TYPE_ARRAY and type_id < TYPE_MAX: 343 | var elements: PackedStringArray = [] 344 | for e: Variant in converted_value: 345 | elements.push_back(__get_value_text(e)) 346 | return ("%s([])" % type) if default else ("%s([%s])" % [type, ", ".join(elements)]) 347 | 348 | _Log.error([_Localize.translate("转换失败,不支持的类型: "), type, " - ", converted_value]) 349 | return "" 350 | 351 | 352 | func _get_tooltip_text() -> String: 353 | return """默认的 GDScript 导入 (TypedArray) 适合表格条目较少的情况。 354 | 可选配置参数: 355 | - generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名 356 | - pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true 357 | """ 358 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | const _Settings = preload("settings.gd") 5 | const _Preset = preload("../scripts/preset.gd") 6 | const _Log = _Preset._Log 7 | const _Localize = _Preset._Localize 8 | 9 | const _AdditionalPropertyEdit = preload("additional_property_edit.gd") 10 | const _PropertyDescriptionEdit = preload("property_description_edit.gd") 11 | const _MetaEdit = preload("meta_edit.gd") 12 | 13 | # 14 | @onready var _file_dialog: FileDialog = %FileDialog 15 | @onready var _confirmation_dialog: ConfirmationDialog = %ConfirmationDialog 16 | 17 | # 预设 18 | @onready var _preset_name_line_edit: LineEdit = %PresetNameLineEdit 19 | 20 | # 操作 21 | @onready var _preset_options: OptionButton = %PresetOption 22 | @onready var _save_btn: Button = %SaveButton 23 | @onready var _delete_btn: Button = %DeleteButton 24 | @onready var _script_select_btn: Button = %ScriptSelectBtn 25 | @onready var _add_ap_btn: Button = %AddAPBtn 26 | @onready var _add_desc_btn: Button = %AddDescBtn 27 | @onready var _add_meta_btn: Button = %AddMetaBtn 28 | @onready var _output_select_btn: Button = %OutputSelectBtn 29 | # @onready var _table_import_select_btn: Button = %TableImportSelectBtn 30 | 31 | # 预设参数 32 | @onready var _class_name_line_edit: LineEdit = %ClassNameLineEdit 33 | @onready var _script_line_edit: LineEdit = %ScriptLineEdit 34 | @onready var _only_storage_check_box: CheckBox = %OnlyStorageCheckBox 35 | @onready var _no_inheritance_check_box: CheckBox = %NoInheritanceCheckBox 36 | @onready var _ascending_check_box: CheckBox = %AscendingCheckBox 37 | @onready var _instantiation_line_edit: LineEdit = %InstantiationLineEdit 38 | @onready var _priority_line_edit: LineEdit = %PriorityLineEdit 39 | @onready var _output_line_edit: LineEdit = %OutputLineEdit 40 | @onready var _table_import_line_edit: LineEdit = %TableImportLineEdit 41 | 42 | @onready var _tab_container: TabContainer = %TabContainer 43 | @onready var _settings: _Settings 44 | @onready var _ap_container: VBoxContainer = %APContainer 45 | @onready var _desc_container: VBoxContainer = %DescContainer 46 | @onready var _meta_container: VBoxContainer = %MetaContainer 47 | 48 | @onready var _table_tool_option_line_edit: LineEdit = %TableOptionsLineEdit 49 | @onready var _import_tool_option_line_edit: LineEdit = %ImportOptionsLineEdit 50 | 51 | @onready var _table_tool_options: OptionButton = %TableToolOptions 52 | @onready var _import_tool_options: OptionButton = %ImportToolOptions 53 | 54 | # @onready var _preset_manager_tab := find_child("预设管理") as Node 55 | @onready var _gen_and_import_tab := find_child("生成与导入") as Node 56 | 57 | var _ap_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("additional_property_edit.tscn")) 58 | var _pd_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("property_description_edit.tscn")) 59 | var _meta_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("meta_edit.tscn")) 60 | 61 | 62 | func _ready() -> void: 63 | _settings = _load((get_script().resource_path as String).get_base_dir().path_join("settings.tscn")).instantiate() 64 | _settings.tools_updated.connect(_on_settings_tools_updated) 65 | _tab_container.add_child(_settings) 66 | 67 | _confirmation_dialog.confirmed.connect(func() -> void: _confirmed.emit(true)) 68 | _confirmation_dialog.canceled.connect(func() -> void: _confirmed.emit(false)) 69 | 70 | _file_dialog.canceled.connect(func() -> void: _path_selected.emit(false, "")) 71 | _file_dialog.dir_selected.connect(func(dir: String) -> void: _path_selected.emit(true, dir)) 72 | _file_dialog.file_selected.connect(func(file: String) -> void: _path_selected.emit(true, file)) 73 | 74 | _preset_options.item_selected.connect(_on_preset_options_selected) 75 | _save_btn.pressed.connect(_on_save_btn_pressed) 76 | _delete_btn.pressed.connect(_on_delete_btn_pressed) 77 | _script_select_btn.pressed.connect(_on_script_select_btn_pressed) 78 | _add_ap_btn.pressed.connect(_on_add_additional_property_btn_pressed) 79 | _add_desc_btn.pressed.connect(_on_add_desc_btn_pressed) 80 | _add_meta_btn.pressed.connect(_on_add_meta_btn_pressed) 81 | _output_select_btn.pressed.connect(_on_output_select_btn_pressed) 82 | %GenerateModifierSelectBtn.pressed.connect(_on_generate_modifier_select_btn_pressed) 83 | %ImportModifierSelectBtn.pressed.connect(_on_import_modifier_select_btn_pressed) 84 | 85 | _gen_and_import_tab.visibility_changed.connect(_on_gen_and_import_tab_visibility_changed) 86 | 87 | _table_tool_options.item_selected.connect(_on_table_tool_options_item_selected) 88 | _import_tool_options.item_selected.connect(_on_import_tool_options_item_selected) 89 | 90 | _preset_options.pressed.connect(_on_preset_options_pressed) 91 | 92 | _preset_options.clear() 93 | _preset_options.add_item(_Localize.translate("- None -")) 94 | _preset_options.set_item_metadata(0, null) 95 | 96 | _Localize.localize_node(self) 97 | _on_settings_tools_updated() 98 | 99 | 100 | # --------------- 101 | signal _path_selected(confirmed: bool, path: String) 102 | signal _confirmed(confirm: bool) 103 | 104 | 105 | #region 内部方法 106 | func _pop_file_dialog(title: String, mode: FileDialog.FileMode, filters: PackedStringArray, path: String) -> void: 107 | _file_dialog.title = title 108 | _file_dialog.file_mode = mode 109 | _file_dialog.filters = filters 110 | _file_dialog.current_path = path 111 | _file_dialog.popup_centered_ratio(0.6) 112 | 113 | 114 | func _on_preset_options_pressed() -> void: 115 | if _preset_options.item_count <= 1: 116 | _refresh_preset_options() 117 | 118 | 119 | func _refresh_preset_options(preset_name: String = "") -> void: 120 | _preset_options.clear() 121 | _preset_options.add_item(_Localize.translate("- None -")) 122 | _preset_options.set_item_metadata(0, null) 123 | if not DirAccess.dir_exists_absolute(_settings.presets_dir): 124 | return 125 | var fs := EditorInterface.get_resource_filesystem() as EditorFileSystem 126 | var dir := fs.get_filesystem_path(_settings.presets_dir) 127 | if not is_instance_valid(dir): 128 | _Log.error([_Localize.translate('无效的预设保存路径"{save_dir}",必须是资源目录下的合法路径。').format({save_dir = _settings.presets_dir})]) 129 | return 130 | 131 | var idx := -1 132 | for i in range(dir.get_file_count()): 133 | if dir.get_file_type(i) != &"Resource": 134 | continue 135 | var preset := _load(dir.get_file_path(i)) as _Preset 136 | if not is_instance_valid(preset): 137 | continue 138 | 139 | _preset_options.add_item(preset.name) 140 | _preset_options.set_item_metadata(_preset_options.item_count - 1, preset) 141 | if preset.name == preset_name: 142 | idx = _preset_options.item_count - 1 143 | 144 | if idx >= 0: 145 | _on_preset_options_selected(idx) 146 | 147 | 148 | func _save_preset() -> void: 149 | var preset_name: String = _preset_name_line_edit.text 150 | if preset_name.is_empty(): 151 | _Log.error([_Localize.translate("预设名称不能为空。")]) 152 | return 153 | if not preset_name.is_valid_filename(): 154 | _Log.error([_Localize.translate('预设名称"{preset_name}"无法作为文件名').format({preset_name = preset_name})]) 155 | return 156 | var presets_dir := _settings.presets_dir 157 | if not DirAccess.dir_exists_absolute(presets_dir): 158 | var make_dir_err := DirAccess.make_dir_recursive_absolute(presets_dir) 159 | if make_dir_err != OK: 160 | _Log.error([_Localize.translate('创建预设路径"{presets_dir}"失败: ').format({presets_dir = presets_dir}), error_string(make_dir_err)]) 161 | return 162 | 163 | var preset := _Preset.new() 164 | _set_to_preset(preset) 165 | 166 | var file_name := preset_name.capitalize().replace(" ", "_").to_lower().trim_suffix(".tres") + ".tres" 167 | var fp := presets_dir.path_join(file_name) 168 | var err := ResourceSaver.save(preset, fp, ResourceSaver.FLAG_NONE) 169 | if err != OK: 170 | _Log.error([_Localize.translate('保存预设失败"{file_name}"失败: ').format({file_name = file_name}), error_string(err)]) 171 | return 172 | 173 | EditorInterface.get_resource_filesystem().update_file(file_name) 174 | _Log.info([_Localize.translate('保存预设"{preset_name}"成功: ').format({preset_name = preset_name})]) 175 | _refresh_preset_options(preset.name) 176 | 177 | 178 | func _delete_preset(preset: _Preset) -> void: 179 | var file_path := preset.resource_path 180 | if not FileAccess.file_exists(file_path): 181 | _Log.error([_Localize.translate("删除失败,预设”{file_path}“不存在").format({file_path = file_path})]) 182 | return 183 | 184 | var err := DirAccess.remove_absolute(file_path) 185 | if not err == OK: 186 | _Log.error([_Localize.translate("删除预设失败: "), error_string(err)]) 187 | return 188 | 189 | _Log.info([_Localize.translate("删除预设成功: "), preset.name]) 190 | EditorInterface.get_resource_filesystem().update_file(file_path) 191 | _refresh_preset_options() 192 | 193 | 194 | func _remove_and_queue_free_children(node: Node) -> void: 195 | for c in node.get_children(): 196 | node.remove_child(c) 197 | c.queue_free() 198 | 199 | 200 | func _load_preset(preset: _Preset) -> void: 201 | _preset_name_line_edit.text = preset.name 202 | _class_name_line_edit.text = preset.data_class 203 | _script_line_edit.text = preset.data_class_script 204 | %TableNameLineEdit.text = preset.table_name.strip_edges() 205 | %SkipPrefixUnderscoreCheckBox.set_pressed_no_signal(preset.skip_prefix_underscore_properties) 206 | _only_storage_check_box.set_pressed_no_signal(preset.only_storage_properties) 207 | _no_inheritance_check_box.set_pressed_no_signal(preset.no_inheritance) 208 | _ascending_check_box.set_pressed_no_signal(preset.ascending_order) 209 | _instantiation_line_edit.text = preset.instantiation 210 | _priority_line_edit.text = ", ".join(preset.priority_properties) 211 | %IgnoreLineEdit.text = ", ".join(preset.ignored_properties) 212 | %MetaPriorityLineEdit.text = ", ".join(preset.need_meta_properties) 213 | _output_line_edit.text = preset.table_output_path 214 | %BackupCheckBox.set_pressed_no_signal(preset.auto_backup) 215 | %MergeCheckBox.set_pressed_no_signal(preset.auto_merge) 216 | _table_import_line_edit.text = preset.import_path 217 | _table_tool_option_line_edit.text = ", ".join(preset.table_tool_options) 218 | _import_tool_option_line_edit.text = ", ".join(preset.import_tool_options) 219 | %GenerateModifierLineEdit.text = preset.generate_modifier_file 220 | %ImportModifierLineEdit.text = preset.import_modifier_file 221 | 222 | _remove_and_queue_free_children(_ap_container) 223 | for ap: Dictionary in preset.additional_properties: 224 | var ape := _ap_edit_scene.instantiate() as _AdditionalPropertyEdit 225 | ape.setup(ap.get("name", ""), ap.get("type", TYPE_BOOL), ap.get("setter", "")) 226 | ape.delete_request.connect(_on_additional_property_delete_request.bind(ape)) 227 | _ap_container.add_child(ape) 228 | 229 | _remove_and_queue_free_children(_desc_container) 230 | for f: String in preset.descriptions: 231 | var pde := _pd_edit_scene.instantiate() as _PropertyDescriptionEdit 232 | pde.setup(f, preset.descriptions[f]) 233 | pde.delete_request.connect(_on_property_description_delete_request.bind(pde)) 234 | _desc_container.add_child(pde) 235 | 236 | _remove_and_queue_free_children(_meta_container) 237 | for m: String in preset.meta_list: 238 | var me := _meta_edit_scene.instantiate() as _MetaEdit 239 | me.setup(m) 240 | me.delete_request.connect(_on_meta_edit_delete_request.bind(me)) 241 | _meta_container.add_child(me) 242 | 243 | for i in range(_table_tool_options.item_count): 244 | if _table_tool_options.get_item_metadata(i) == preset.table_tool_script_file: 245 | _table_tool_options.select(i) 246 | break 247 | 248 | for i in range(_import_tool_options.item_count): 249 | if _import_tool_options.get_item_metadata(i) == preset.import_tool_script_file: 250 | _import_tool_options.select(i) 251 | _import_tool_options.item_selected.emit(i) 252 | break 253 | 254 | 255 | func _set_to_preset(preset: _Preset) -> void: 256 | preset.name = _preset_name_line_edit.text 257 | preset.data_class = _class_name_line_edit.text 258 | preset.data_class_script = _script_line_edit.text 259 | preset.table_name = %TableNameLineEdit.text.strip_edges() 260 | preset.skip_prefix_underscore_properties = %SkipPrefixUnderscoreCheckBox.button_pressed 261 | preset.only_storage_properties = _only_storage_check_box.button_pressed 262 | preset.no_inheritance = _no_inheritance_check_box.button_pressed 263 | preset.ascending_order = _ascending_check_box.button_pressed 264 | preset.instantiation = _instantiation_line_edit.text 265 | preset.priority_properties = Array(_priority_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges()) 266 | preset.ignored_properties = Array(%IgnoreLineEdit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges()) 267 | preset.table_output_path = _output_line_edit.text 268 | preset.need_meta_properties = Array(%MetaPriorityLineEdit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges()) 269 | preset.auto_backup = %BackupCheckBox.button_pressed 270 | preset.auto_merge = %MergeCheckBox.button_pressed 271 | preset.import_path = _table_import_line_edit.text 272 | preset.table_tool_options = Array(_table_tool_option_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges()) 273 | preset.import_tool_options = Array(_import_tool_option_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges()) 274 | preset.generate_modifier_file = %GenerateModifierLineEdit.text 275 | preset.import_modifier_file = %ImportModifierLineEdit.text 276 | 277 | preset.additional_properties.clear() 278 | for ape in _ap_container.get_children(): 279 | ape = ape as _AdditionalPropertyEdit 280 | if not is_instance_valid(ape): 281 | continue 282 | var ap := {} # _Preset._AdditionalProperty.new() 283 | ap["name"] = ape.get_property_name() 284 | ap["type"] = ape.get_type() 285 | ap["setter"] = ape.get_setter() 286 | preset.additional_properties.push_back(ap) 287 | 288 | preset.descriptions.clear() 289 | for pde in _desc_container.get_children(): 290 | pde = pde as _PropertyDescriptionEdit 291 | if not is_instance_valid(pde): 292 | continue 293 | if preset.descriptions.has(pde.get_property_name()): 294 | _Log.warning([_Localize.translate("重复的字段描述将被跳过: "), pde.get_property_name(), " - ", pde.get_description()]) 295 | continue 296 | preset.descriptions[pde.get_property_name()] = pde.get_description() 297 | 298 | preset.meta_list.clear() 299 | for me in _meta_container.get_children(): 300 | me = me as _MetaEdit 301 | if not is_instance_valid(me): 302 | continue 303 | preset.meta_list.push_back(me.get_meta_text()) 304 | 305 | var m: Variant = _table_tool_options.get_selected_metadata() 306 | if typeof(m) == TYPE_STRING: 307 | preset.table_tool_script_file = m 308 | else: 309 | preset.table_tool_script_file = "" 310 | 311 | m = _import_tool_options.get_selected_metadata() 312 | if typeof(m) == TYPE_STRING: 313 | preset.import_tool_script_file = m 314 | else: 315 | preset.import_tool_script_file = "" 316 | 317 | 318 | #endregion 319 | 320 | 321 | #-------------------------------------- 322 | #region 回调 323 | func _on_preset_options_selected(idx: int) -> void: 324 | var preset: _Preset 325 | if idx == 0: 326 | preset = _Preset.new() 327 | else: 328 | preset = _preset_options.get_item_metadata(idx) 329 | if not is_instance_valid(preset): 330 | _Log.error([_Localize.translate("Bug,请提交issue并提供复现步骤")]) 331 | return 332 | _load_preset(preset) 333 | 334 | 335 | func _on_save_btn_pressed() -> void: 336 | _save_preset() 337 | 338 | 339 | func _on_delete_btn_pressed() -> void: 340 | if _preset_options.selected < 0: 341 | _Log.error([_Localize.translate("删除预设失败,未选中预设")]) 342 | return 343 | 344 | _confirmation_dialog.popup_centered() 345 | if not await _confirmed: 346 | return 347 | 348 | var preset := _preset_options.get_item_metadata(_preset_options.selected) as _Preset 349 | if not is_instance_valid(preset): 350 | _Log.error([_Localize.translate("Bug,请提交issue并提供复现步骤")]) 351 | return 352 | 353 | _delete_preset(preset) 354 | 355 | 356 | func _on_script_select_btn_pressed() -> void: 357 | var path := _script_line_edit.text 358 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path) 359 | var result := await _path_selected as Array 360 | if not result[0]: 361 | return 362 | _script_line_edit.text = result[1] 363 | 364 | 365 | func _on_generate_modifier_select_btn_pressed() -> void: 366 | var path := %GenerateModifierLineEdit.text as String 367 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path) 368 | var result := await _path_selected as Array 369 | if not result[0]: 370 | return 371 | %GenerateModifierLineEdit.text = result[1] 372 | 373 | 374 | func _on_import_modifier_select_btn_pressed() -> void: 375 | var path := %ImportModifierLineEdit.text as String 376 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path) 377 | var result := await _path_selected as Array 378 | if not result[0]: 379 | return 380 | %ImportModifierLineEdit.text = result[1] 381 | 382 | 383 | func _on_add_additional_property_btn_pressed() -> void: 384 | var ape := _ap_edit_scene.instantiate() as _AdditionalPropertyEdit 385 | ape.delete_request.connect(_on_additional_property_delete_request.bind(ape)) 386 | _ap_container.add_child(ape) 387 | 388 | 389 | func _on_add_desc_btn_pressed() -> void: 390 | var pde := _pd_edit_scene.instantiate() as _PropertyDescriptionEdit 391 | pde.delete_request.connect(_on_property_description_delete_request.bind(pde)) 392 | _desc_container.add_child(pde) 393 | 394 | 395 | func _on_add_meta_btn_pressed() -> void: 396 | var me := _meta_edit_scene.instantiate() as _MetaEdit 397 | me.delete_request.connect(_on_meta_edit_delete_request.bind(me)) 398 | _meta_container.add_child(me) 399 | 400 | 401 | func _on_output_select_btn_pressed() -> void: 402 | var path := _output_line_edit.text as String 403 | # "*.csv;CSV表格" 404 | _pop_file_dialog(_Localize.translate("输出文件"), FileDialog.FILE_MODE_OPEN_FILE, [], path) 405 | var result := await _path_selected as Array 406 | if not result[0]: 407 | return 408 | _output_line_edit.text = result[1] 409 | 410 | 411 | func _on_additional_property_delete_request(ape: _AdditionalPropertyEdit) -> void: 412 | _ap_container.remove_child(ape) 413 | ape.queue_free() 414 | 415 | 416 | func _on_property_description_delete_request(pde: _PropertyDescriptionEdit) -> void: 417 | _desc_container.remove_child(pde) 418 | pde.queue_free() 419 | 420 | 421 | func _on_meta_edit_delete_request(me: _MetaEdit) -> void: 422 | _meta_container.remove_child(me) 423 | me.queue_free() 424 | 425 | 426 | func _on_settings_tools_updated() -> void: 427 | var selecting_table_tool: Variant = _table_tool_options.get_selected_metadata() 428 | _table_tool_options.clear() 429 | var table_tools := _settings.table_tools 430 | var table_option_idx := -1 431 | for tool_name: String in table_tools: 432 | var tool_path := table_tools[tool_name] as String 433 | var tool_script := load(tool_path) as Script 434 | _table_tool_options.add_item("%s: %s" % [_Localize.translate(tool_name), tool_path]) 435 | _table_tool_options.set_item_metadata(_table_tool_options.item_count - 1, tool_path) 436 | _table_tool_options.set_item_tooltip(_table_tool_options.item_count - 1, tool_script.new().get_tooltip_text()) 437 | if selecting_table_tool == tool_path: 438 | table_option_idx = _table_tool_options.item_count - 1 439 | if table_option_idx >= 0: 440 | _table_tool_options.select(table_option_idx) 441 | _table_tool_options.item_selected.emit(table_option_idx) 442 | 443 | var selecting_import_tool: Variant = _import_tool_options.get_selected_metadata() 444 | _import_tool_options.clear() 445 | var import_option_idx := -1 446 | var import_tools := _settings.import_tools 447 | for tool_name: String in import_tools: 448 | var tool_path := import_tools[tool_name] as String 449 | var tool_script := load(tool_path) as Script 450 | _import_tool_options.add_item("%s: %s" % [_Localize.translate(tool_name), tool_path]) 451 | _import_tool_options.set_item_metadata(_import_tool_options.item_count - 1, tool_path) 452 | _import_tool_options.set_item_tooltip(_import_tool_options.item_count - 1, tool_script.new().get_tooltip_text()) 453 | if selecting_import_tool == tool_path: 454 | import_option_idx = _import_tool_options.item_count - 1 455 | if import_option_idx >= 0: 456 | _import_tool_options.select(import_option_idx) 457 | _import_tool_options.item_selected.emit(import_option_idx) 458 | 459 | 460 | func _on_table_tool_options_item_selected(idx: int) -> void: 461 | var tooltip := "" 462 | var meta: Variant = _table_tool_options.get_item_metadata(idx) 463 | if meta == null or not meta is String: 464 | tooltip = "" 465 | else: 466 | var script := load(meta) as Script 467 | tooltip = script.new().get_tooltip_text() 468 | _table_tool_options.tooltip_text = tooltip 469 | _table_tool_option_line_edit.tooltip_text = tooltip 470 | 471 | 472 | func _on_import_tool_options_item_selected(idx: int) -> void: 473 | var tooltip := "" 474 | var meta: Variant = _import_tool_options.get_item_metadata(idx) 475 | if meta == null or not meta is String: 476 | tooltip = "" 477 | else: 478 | var script := load(meta) as Script 479 | tooltip = script.new().get_tooltip_text() 480 | _import_tool_options.tooltip_text = tooltip 481 | _import_tool_option_line_edit.tooltip_text = tooltip 482 | 483 | 484 | func _on_gen_and_import_tab_visibility_changed() -> void: 485 | if not _gen_and_import_tab.visible: 486 | return 487 | 488 | var presets: Array[_Preset] = [] 489 | var dir := EditorInterface.get_resource_filesystem().get_filesystem_path(_settings.presets_dir) 490 | if is_instance_valid(dir): 491 | for i in range(dir.get_file_count()): 492 | if dir.get_file_type(i) != &"Resource": 493 | continue 494 | var preset := _load(dir.get_file_path(i)) as _Preset 495 | if not is_instance_valid(preset): 496 | continue 497 | 498 | presets.append(preset) 499 | 500 | %GenerateTables.save_path = EditorInterface.get_editor_paths().get_project_settings_dir().path_join(".ctm_generate_settings") 501 | %ImportTables.save_path = EditorInterface.get_editor_paths().get_project_settings_dir().path_join(".ctm_import_settings") 502 | 503 | %GenerateTables.setup(presets) 504 | %ImportTables.setup(presets) 505 | 506 | 507 | #endregion 508 | 509 | 510 | #-------------------------------------- 511 | func _load(path: String) -> Resource: 512 | var ret := ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE) 513 | if is_instance_valid(ret) and ret.resource_path.is_empty(): 514 | # 兼容 4.2 515 | ret.take_over_path(path) 516 | #ret.resource_path = path 517 | return ret 518 | 519 | 520 | func _get_script_filters() -> PackedStringArray: 521 | var ret: PackedStringArray = ["*.gd;GDScript"] 522 | if ClassDB.class_exists(&"CSharpScript"): 523 | ret.push_back("*.cs;CSharpScript") 524 | for klass in ClassDB.get_inheriters_from_class(&"ScriptLanguageExtension"): 525 | if not ClassDB.can_instantiate(klass): 526 | continue 527 | var script := ClassDB.instantiate(klass) as ScriptExtension 528 | if not is_instance_valid(script): 529 | continue 530 | var language := script._get_language() as ScriptLanguageExtension 531 | if not is_instance_valid(language): 532 | continue 533 | var extension := language._get_extension() as String 534 | var script_name := language._get_name() as String 535 | if extension.is_empty() or script_name.is_empty(): 536 | continue 537 | ret.push_back("*.%s;%s" % [extension, script_name]) 538 | return ret 539 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cl4vm3gw0ltsi"] 2 | 3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd" id="1_bsud7"] 4 | [ext_resource type="PackedScene" uid="uid://bepjlsptadkrq" path="res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn" id="2_sf0g2"] 5 | 6 | [node name="MainScreen" type="PanelContainer"] 7 | custom_minimum_size = Vector2(500, 300) 8 | anchors_preset = 15 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | grow_horizontal = 2 12 | grow_vertical = 2 13 | size_flags_horizontal = 3 14 | size_flags_vertical = 3 15 | script = ExtResource("1_bsud7") 16 | 17 | [node name="TabContainer" type="TabContainer" parent="."] 18 | unique_name_in_owner = true 19 | layout_mode = 2 20 | drag_to_rearrange_enabled = true 21 | 22 | [node name="预设管理" type="PanelContainer" parent="TabContainer"] 23 | layout_mode = 2 24 | metadata/_tab_index = 0 25 | 26 | [node name="MarginContainer" type="MarginContainer" parent="TabContainer/预设管理"] 27 | layout_mode = 2 28 | theme_override_constants/margin_left = 5 29 | theme_override_constants/margin_top = 5 30 | theme_override_constants/margin_right = 5 31 | theme_override_constants/margin_bottom = 5 32 | 33 | [node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer"] 34 | layout_mode = 2 35 | theme_override_constants/separation = 23 36 | 37 | [node name="GridContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer"] 38 | layout_mode = 2 39 | size_flags_vertical = 0 40 | 41 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 42 | layout_mode = 2 43 | text = "预设:" 44 | 45 | [node name="PresetOption" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 46 | unique_name_in_owner = true 47 | layout_mode = 2 48 | size_flags_horizontal = 3 49 | item_count = 1 50 | selected = 0 51 | popup/item_0/text = "- None -" 52 | popup/item_0/id = 0 53 | 54 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 55 | layout_mode = 2 56 | text = "预设名称: " 57 | 58 | [node name="PresetNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 59 | unique_name_in_owner = true 60 | layout_mode = 2 61 | size_flags_horizontal = 3 62 | placeholder_text = "要保存的预设名称" 63 | 64 | [node name="SaveButton" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 65 | unique_name_in_owner = true 66 | layout_mode = 2 67 | text = "保存" 68 | 69 | [node name="DeleteButton" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"] 70 | unique_name_in_owner = true 71 | layout_mode = 2 72 | text = "删除" 73 | 74 | [node name="ScrollContainer2" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer"] 75 | layout_mode = 2 76 | size_flags_vertical = 3 77 | 78 | [node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2"] 79 | layout_mode = 2 80 | size_flags_horizontal = 3 81 | size_flags_vertical = 3 82 | 83 | [node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"] 84 | layout_mode = 2 85 | 86 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer"] 87 | layout_mode = 2 88 | text = "数据类名:" 89 | 90 | [node name="ClassNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer"] 91 | unique_name_in_owner = true 92 | layout_mode = 2 93 | size_flags_horizontal = 3 94 | placeholder_text = "原生类名或脚本的内部类名,留空时脚本不能为空" 95 | 96 | [node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"] 97 | layout_mode = 2 98 | 99 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"] 100 | layout_mode = 2 101 | text = "数据类脚本:" 102 | 103 | [node name="ScriptLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"] 104 | unique_name_in_owner = true 105 | layout_mode = 2 106 | size_flags_horizontal = 3 107 | placeholder_text = "可选项" 108 | 109 | [node name="ScriptSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"] 110 | unique_name_in_owner = true 111 | layout_mode = 2 112 | text = "选择" 113 | 114 | [node name="HBoxContainer6" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"] 115 | layout_mode = 2 116 | 117 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer6"] 118 | layout_mode = 2 119 | text = "表格名:" 120 | 121 | [node name="TableNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer6"] 122 | unique_name_in_owner = true 123 | layout_mode = 2 124 | size_flags_horizontal = 3 125 | placeholder_text = "建议使用大驼峰命名" 126 | 127 | [node name="PanelContainer" type="PanelContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"] 128 | layout_mode = 2 129 | size_flags_vertical = 3 130 | 131 | [node name="TabContainer" type="TabContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer"] 132 | layout_mode = 2 133 | size_flags_vertical = 3 134 | tab_alignment = 2 135 | 136 | [node name="表格选项" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"] 137 | layout_mode = 2 138 | metadata/_tab_index = 0 139 | 140 | [node name="HBoxContainer5" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 141 | layout_mode = 2 142 | 143 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer5"] 144 | layout_mode = 2 145 | text = "优先字段:" 146 | 147 | [node name="PriorityLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer5"] 148 | unique_name_in_owner = true 149 | layout_mode = 2 150 | size_flags_horizontal = 3 151 | placeholder_text = "用\",\"分隔,未列入的字段将排在其后。例:id, name, description" 152 | 153 | [node name="HBoxContainer8" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 154 | layout_mode = 2 155 | 156 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer8"] 157 | layout_mode = 2 158 | text = "忽略字段:" 159 | 160 | [node name="IgnoreLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer8"] 161 | unique_name_in_owner = true 162 | layout_mode = 2 163 | size_flags_horizontal = 3 164 | placeholder_text = "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data" 165 | 166 | [node name="HBoxContainer6" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 167 | layout_mode = 2 168 | 169 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer6"] 170 | layout_mode = 2 171 | text = "需要元字段的字段: " 172 | 173 | [node name="MetaPriorityLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer6"] 174 | unique_name_in_owner = true 175 | layout_mode = 2 176 | size_flags_horizontal = 3 177 | placeholder_text = "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。" 178 | 179 | [node name="HBoxContainer3" type="HFlowContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 180 | layout_mode = 2 181 | 182 | [node name="SkipPrefixUnderscoreCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 183 | unique_name_in_owner = true 184 | layout_mode = 2 185 | button_pressed = true 186 | text = "跳过\"_\"开头的属性" 187 | 188 | [node name="OnlyStorageCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 189 | unique_name_in_owner = true 190 | layout_mode = 2 191 | tooltip_text = "即含有PROPERTY_USAGE_STORAGE的属性" 192 | text = "仅需要存储的属性" 193 | 194 | [node name="NoInheritanceCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 195 | unique_name_in_owner = true 196 | layout_mode = 2 197 | button_pressed = true 198 | text = "不含父类属性" 199 | 200 | [node name="AscendingCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 201 | unique_name_in_owner = true 202 | layout_mode = 2 203 | text = "按字母升序" 204 | 205 | [node name="BackupCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 206 | unique_name_in_owner = true 207 | layout_mode = 2 208 | tooltip_text = "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。" 209 | text = "覆盖前备份" 210 | 211 | [node name="MergeCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"] 212 | unique_name_in_owner = true 213 | layout_mode = 2 214 | tooltip_text = "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并" 215 | button_pressed = true 216 | text = "自动合并" 217 | 218 | [node name="GridContainer2" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 219 | layout_mode = 2 220 | size_flags_vertical = 10 221 | columns = 3 222 | 223 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"] 224 | layout_mode = 2 225 | text = "表格生成路径:" 226 | 227 | [node name="OutputLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"] 228 | unique_name_in_owner = true 229 | layout_mode = 2 230 | size_flags_horizontal = 3 231 | text = "res://tables/{table_name}.csv" 232 | 233 | [node name="OutputSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"] 234 | unique_name_in_owner = true 235 | layout_mode = 2 236 | text = "选择" 237 | 238 | [node name="HBoxContainer7" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 239 | layout_mode = 2 240 | size_flags_vertical = 8 241 | alignment = 2 242 | 243 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer7"] 244 | layout_mode = 2 245 | text = "表格选项参数:" 246 | 247 | [node name="TableOptionsLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer7"] 248 | unique_name_in_owner = true 249 | layout_mode = 2 250 | size_flags_horizontal = 3 251 | placeholder_text = "特定于表格工具的选项参数,用\",\"分隔。" 252 | 253 | [node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 254 | layout_mode = 2 255 | 256 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer"] 257 | layout_mode = 2 258 | text = "表格工具:" 259 | 260 | [node name="TableToolOptions" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer"] 261 | unique_name_in_owner = true 262 | layout_mode = 2 263 | size_flags_horizontal = 3 264 | item_count = 2 265 | selected = 0 266 | popup/item_0/text = "CSV(,分隔): res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd" 267 | popup/item_0/id = 0 268 | popup/item_1/text = "Excel(xlsx): res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd" 269 | popup/item_1/id = 1 270 | 271 | [node name="GridContainer3" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"] 272 | layout_mode = 2 273 | size_flags_vertical = 8 274 | columns = 3 275 | 276 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"] 277 | layout_mode = 2 278 | text = "表格生成修改器:" 279 | 280 | [node name="GenerateModifierLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"] 281 | unique_name_in_owner = true 282 | layout_mode = 2 283 | size_flags_horizontal = 3 284 | placeholder_text = "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd" 285 | 286 | [node name="GenerateModifierSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"] 287 | unique_name_in_owner = true 288 | layout_mode = 2 289 | text = "选择" 290 | 291 | [node name="导入选项" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"] 292 | visible = false 293 | layout_mode = 2 294 | metadata/_tab_index = 1 295 | 296 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"] 297 | layout_mode = 2 298 | 299 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer4"] 300 | layout_mode = 2 301 | text = "数据对象实例化:" 302 | 303 | [node name="InstantiationLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer4"] 304 | unique_name_in_owner = true 305 | layout_mode = 2 306 | size_flags_horizontal = 3 307 | placeholder_text = "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造" 308 | 309 | [node name="GridContainer2" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"] 310 | layout_mode = 2 311 | size_flags_vertical = 10 312 | columns = 3 313 | 314 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"] 315 | layout_mode = 2 316 | text = "导入路径:" 317 | 318 | [node name="TableImportLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"] 319 | unique_name_in_owner = true 320 | layout_mode = 2 321 | size_flags_horizontal = 3 322 | text = "res://tables/imported/{table_name}.gd" 323 | 324 | [node name="TableImportSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"] 325 | unique_name_in_owner = true 326 | layout_mode = 2 327 | text = "选择" 328 | 329 | [node name="HBoxContainer7" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"] 330 | layout_mode = 2 331 | size_flags_vertical = 8 332 | alignment = 2 333 | 334 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer7"] 335 | layout_mode = 2 336 | text = "导入选项参数:" 337 | 338 | [node name="ImportOptionsLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer7"] 339 | unique_name_in_owner = true 340 | layout_mode = 2 341 | size_flags_horizontal = 3 342 | tooltip_text = "特定于导入工具的选项参数,用\",\"分隔。 343 | 例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。" 344 | placeholder_text = "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name" 345 | 346 | [node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"] 347 | layout_mode = 2 348 | 349 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer2"] 350 | layout_mode = 2 351 | text = "导入工具:" 352 | 353 | [node name="ImportToolOptions" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer2"] 354 | unique_name_in_owner = true 355 | layout_mode = 2 356 | size_flags_horizontal = 3 357 | item_count = 2 358 | selected = 0 359 | allow_reselect = true 360 | popup/item_0/text = "GDScript(TypedArray风格): res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd" 361 | popup/item_0/id = 0 362 | popup/item_1/text = "GDScript(Dictionary风格): res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd" 363 | popup/item_1/id = 1 364 | 365 | [node name="GridContainer3" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"] 366 | layout_mode = 2 367 | size_flags_vertical = 8 368 | columns = 3 369 | 370 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"] 371 | layout_mode = 2 372 | text = "导入修改器:" 373 | 374 | [node name="ImportModifierLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"] 375 | unique_name_in_owner = true 376 | layout_mode = 2 377 | size_flags_horizontal = 3 378 | placeholder_text = "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd" 379 | 380 | [node name="ImportModifierSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"] 381 | unique_name_in_owner = true 382 | layout_mode = 2 383 | text = "选择" 384 | 385 | [node name="附加字段" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"] 386 | visible = false 387 | layout_mode = 2 388 | metadata/_tab_index = 2 389 | 390 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段"] 391 | layout_mode = 2 392 | alignment = 2 393 | 394 | [node name="AddAPBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段/HBoxContainer4"] 395 | unique_name_in_owner = true 396 | layout_mode = 2 397 | text = "+" 398 | 399 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段"] 400 | layout_mode = 2 401 | size_flags_vertical = 3 402 | 403 | [node name="APContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段/ScrollContainer"] 404 | unique_name_in_owner = true 405 | layout_mode = 2 406 | size_flags_horizontal = 3 407 | size_flags_vertical = 3 408 | 409 | [node name="字段描述" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"] 410 | visible = false 411 | layout_mode = 2 412 | metadata/_tab_index = 3 413 | 414 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述"] 415 | layout_mode = 2 416 | alignment = 2 417 | 418 | [node name="AddDescBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述/HBoxContainer4"] 419 | unique_name_in_owner = true 420 | layout_mode = 2 421 | text = "+" 422 | 423 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述"] 424 | layout_mode = 2 425 | size_flags_vertical = 3 426 | 427 | [node name="DescContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述/ScrollContainer"] 428 | unique_name_in_owner = true 429 | layout_mode = 2 430 | size_flags_horizontal = 3 431 | size_flags_vertical = 3 432 | 433 | [node name="自定义元数据" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"] 434 | visible = false 435 | layout_mode = 2 436 | metadata/_tab_index = 4 437 | 438 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据"] 439 | layout_mode = 2 440 | alignment = 2 441 | 442 | [node name="AddMetaBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据/HBoxContainer4"] 443 | unique_name_in_owner = true 444 | layout_mode = 2 445 | text = "+" 446 | 447 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据"] 448 | layout_mode = 2 449 | size_flags_vertical = 3 450 | 451 | [node name="MetaContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据/ScrollContainer"] 452 | unique_name_in_owner = true 453 | layout_mode = 2 454 | size_flags_horizontal = 3 455 | size_flags_vertical = 3 456 | 457 | [node name="生成与导入" type="MarginContainer" parent="TabContainer"] 458 | unique_name_in_owner = true 459 | visible = false 460 | layout_mode = 2 461 | theme_override_constants/margin_left = 10 462 | theme_override_constants/margin_top = 10 463 | theme_override_constants/margin_right = 10 464 | theme_override_constants/margin_bottom = 10 465 | metadata/_tab_index = 1 466 | 467 | [node name="HBoxContainer" type="HSplitContainer" parent="TabContainer/生成与导入"] 468 | layout_mode = 2 469 | 470 | [node name="GenerateTables" parent="TabContainer/生成与导入/HBoxContainer" instance=ExtResource("2_sf0g2")] 471 | unique_name_in_owner = true 472 | layout_mode = 2 473 | 474 | [node name="ImportTables" parent="TabContainer/生成与导入/HBoxContainer" instance=ExtResource("2_sf0g2")] 475 | unique_name_in_owner = true 476 | layout_mode = 2 477 | title = "表格导入" 478 | mode = 1 479 | 480 | [node name="FileDialog" type="FileDialog" parent="."] 481 | unique_name_in_owner = true 482 | size = Vector2i(384, 210) 483 | root_subfolder = "res://" 484 | 485 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] 486 | unique_name_in_owner = true 487 | size = Vector2i(200, 106) 488 | dialog_text = "确认操作?" 489 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/localization/template.pot: -------------------------------------------------------------------------------- 1 | # LANGUAGE translation for Config Table Manager for the following files: 2 | # res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 3 | # res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd 4 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd 5 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 6 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 7 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 8 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 9 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 10 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd 11 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 12 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd 13 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 14 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd 15 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 16 | # res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd 17 | # res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd 18 | # res://addons/config_table_manager.daylily-zeleen/scripts/log.gd 19 | # res://addons/config_table_manager.daylily-zeleen/scripts/preset.gd 20 | # res://addons/config_table_manager.daylily-zeleen/scripts/table_header.gd 21 | # res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 22 | # res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 23 | # res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 24 | # res://addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd 25 | # 26 | # FIRST AUTHOR , YEAR. 27 | # 28 | #, fuzzy 29 | msgid "" 30 | msgstr "" 31 | "Project-Id-Version: Config Table Manager\n" 32 | "MIME-Version: 1.0\n" 33 | "Content-Type: text/plain; charset=UTF-8\n" 34 | "Content-Transfer-Encoding: 8-bit\n" 35 | 36 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 37 | msgid "导表失败: " 38 | msgstr "" 39 | 40 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 41 | msgid "无法被赋值的字段将被跳过: " 42 | msgstr "" 43 | 44 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 45 | msgid "转换失败,不支持的类型: " 46 | msgstr "" 47 | 48 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 49 | msgid "未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。" 50 | msgstr "" 51 | 52 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 53 | msgid "数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。" 54 | msgstr "" 55 | 56 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 57 | msgid "存在未配置key (%s)的数据: %s。" 58 | msgstr "" 59 | 60 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 61 | msgid "存在key (%s)重复的数据,重复的值为: %s。" 62 | msgstr "" 63 | 64 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 65 | msgid "要附加的属性名称" 66 | msgstr "" 67 | 68 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 69 | msgid "非空将使用自定义Setter进行设置" 70 | msgstr "" 71 | 72 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 73 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 74 | msgid " - " 75 | msgstr "" 76 | 77 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 78 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 79 | msgid "选中" 80 | msgstr "" 81 | 82 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 83 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 84 | msgid "排除" 85 | msgstr "" 86 | 87 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 88 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 89 | msgid "预设" 90 | msgstr "" 91 | 92 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 93 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 94 | msgid "表格预设不存在: " 95 | msgstr "" 96 | 97 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 98 | msgid "表格生成" 99 | msgstr "" 100 | 101 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 102 | msgid "对选中项进行生成" 103 | msgstr "" 104 | 105 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 106 | msgid "对所有项进行生成" 107 | msgstr "" 108 | 109 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 110 | msgid "对选中项执行导入" 111 | msgstr "" 112 | 113 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 114 | msgid "对所有项执行导入" 115 | msgstr "" 116 | 117 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 118 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 119 | msgid "无效的预设保存路径\"{save_dir}\",必须是资源目录下的合法路径。" 120 | msgstr "" 121 | 122 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 123 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 124 | msgid "预设名称不能为空。" 125 | msgstr "" 126 | 127 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 128 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 129 | msgid "预设名称\"{preset_name}\"无法作为文件名" 130 | msgstr "" 131 | 132 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 133 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 134 | msgid "创建预设路径\"{presets_dir}\"失败: " 135 | msgstr "" 136 | 137 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 138 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 139 | msgid "保存预设失败\"{file_name}\"失败: " 140 | msgstr "" 141 | 142 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 143 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 144 | msgid "保存预设\"{preset_name}\"成功: " 145 | msgstr "" 146 | 147 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 148 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 149 | msgid "删除失败,预设”{file_path}“不存在" 150 | msgstr "" 151 | 152 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 153 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 154 | msgid "删除预设失败: " 155 | msgstr "" 156 | 157 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 158 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 159 | msgid "删除预设成功: " 160 | msgstr "" 161 | 162 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 163 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 164 | msgid "重复的字段描述将被跳过: " 165 | msgstr "" 166 | 167 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 168 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 169 | msgid "Bug,请提交issue并提供复现步骤" 170 | msgstr "" 171 | 172 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 173 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 174 | msgid "删除预设失败,未选中预设" 175 | msgstr "" 176 | 177 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 178 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 179 | msgid "选择脚本" 180 | msgstr "" 181 | 182 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 183 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 184 | msgid "输出文件" 185 | msgstr "" 186 | 187 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 188 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 189 | msgid "- None -" 190 | msgstr "" 191 | 192 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 193 | msgid "预设:" 194 | msgstr "" 195 | 196 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 197 | msgid "预设名称: " 198 | msgstr "" 199 | 200 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 201 | msgid "要保存的预设名称" 202 | msgstr "" 203 | 204 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 205 | msgid "保存" 206 | msgstr "" 207 | 208 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 209 | msgid "删除" 210 | msgstr "" 211 | 212 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 213 | msgid "数据类名:" 214 | msgstr "" 215 | 216 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 217 | msgid "原生类名或脚本的内部类名,留空时脚本不能为空" 218 | msgstr "" 219 | 220 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 221 | msgid "数据类脚本:" 222 | msgstr "" 223 | 224 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 225 | msgid "可选项" 226 | msgstr "" 227 | 228 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 229 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 230 | msgid "选择" 231 | msgstr "" 232 | 233 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 234 | msgid "表格名:" 235 | msgstr "" 236 | 237 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 238 | msgid "建议使用大驼峰命名" 239 | msgstr "" 240 | 241 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 242 | msgid "优先字段:" 243 | msgstr "" 244 | 245 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 246 | msgid "用\",\"分隔,未列入的字段将排在其后。例:id, name, description" 247 | msgstr "" 248 | 249 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 250 | msgid "忽略字段:" 251 | msgstr "" 252 | 253 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 254 | msgid "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data" 255 | msgstr "" 256 | 257 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 258 | msgid "需要元字段的字段: " 259 | msgstr "" 260 | 261 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 262 | msgid "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。" 263 | msgstr "" 264 | 265 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 266 | msgid "跳过\"_\"开头的属性" 267 | msgstr "" 268 | 269 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 270 | msgid "即含有PROPERTY_USAGE_STORAGE的属性" 271 | msgstr "" 272 | 273 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 274 | msgid "仅需要存储的属性" 275 | msgstr "" 276 | 277 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 278 | msgid "不含父类属性" 279 | msgstr "" 280 | 281 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 282 | msgid "按字母升序" 283 | msgstr "" 284 | 285 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 286 | msgid "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。" 287 | msgstr "" 288 | 289 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 290 | msgid "覆盖前备份" 291 | msgstr "" 292 | 293 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 294 | msgid "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并" 295 | msgstr "" 296 | 297 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 298 | msgid "自动合并" 299 | msgstr "" 300 | 301 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 302 | msgid "表格生成路径:" 303 | msgstr "" 304 | 305 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 306 | msgid "表格选项参数:" 307 | msgstr "" 308 | 309 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 310 | msgid "特定于表格工具的选项参数,用\",\"分隔。" 311 | msgstr "" 312 | 313 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 314 | msgid "表格工具:" 315 | msgstr "" 316 | 317 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 318 | msgid "表格生成修改器:" 319 | msgstr "" 320 | 321 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 322 | msgid "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd" 323 | msgstr "" 324 | 325 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 326 | msgid "数据对象实例化:" 327 | msgstr "" 328 | 329 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 330 | msgid "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造" 331 | msgstr "" 332 | 333 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 334 | msgid "导入路径:" 335 | msgstr "" 336 | 337 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 338 | msgid "导入选项参数:" 339 | msgstr "" 340 | 341 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 342 | msgid "" 343 | "特定于导入工具的选项参数,用\",\"分隔。\n" 344 | "例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。" 345 | msgstr "" 346 | 347 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 348 | msgid "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name" 349 | msgstr "" 350 | 351 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 352 | msgid "导入工具:" 353 | msgstr "" 354 | 355 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 356 | msgid "导入修改器:" 357 | msgstr "" 358 | 359 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 360 | msgid "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd" 361 | msgstr "" 362 | 363 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 364 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 365 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 366 | msgid "+" 367 | msgstr "" 368 | 369 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 370 | msgid "确认操作?" 371 | msgstr "" 372 | 373 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 374 | msgid "预设管理" 375 | msgstr "" 376 | 377 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 378 | msgid "生成与导入" 379 | msgstr "" 380 | 381 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 382 | msgid "设置" 383 | msgstr "" 384 | 385 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 386 | msgid "表格选项" 387 | msgstr "" 388 | 389 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 390 | msgid "导入选项" 391 | msgstr "" 392 | 393 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 394 | msgid "附加字段" 395 | msgstr "" 396 | 397 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 398 | msgid "字段描述" 399 | msgstr "" 400 | 401 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 402 | msgid "自定义元数据" 403 | msgstr "" 404 | 405 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 406 | msgid "表格导入" 407 | msgstr "" 408 | 409 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 410 | msgid "Meta" 411 | msgstr "" 412 | 413 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 414 | msgid "X" 415 | msgstr "" 416 | 417 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 418 | msgid "字段名称" 419 | msgstr "" 420 | 421 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 422 | msgid "描述" 423 | msgstr "" 424 | 425 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 426 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 427 | msgid "表格工具" 428 | msgstr "" 429 | 430 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 431 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 432 | msgid "脚本路径" 433 | msgstr "" 434 | 435 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 436 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 437 | msgid "导入工具" 438 | msgstr "" 439 | 440 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 441 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 442 | msgid "保存设置失败: " 443 | msgstr "" 444 | 445 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 446 | msgid "预设保存路径:" 447 | msgstr "" 448 | 449 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 450 | msgid "保存设置" 451 | msgstr "" 452 | 453 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 454 | msgid "Open a Directory" 455 | msgstr "" 456 | 457 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 458 | msgid "CSV(,分隔)" 459 | msgstr "" 460 | 461 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 462 | msgid "Excel(xlsx)" 463 | msgstr "" 464 | 465 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 466 | msgid "GDScript(TypedArray风格)" 467 | msgstr "" 468 | 469 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 470 | msgid "GDScript(Dictionary风格)" 471 | msgstr "" 472 | 473 | #: addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd 474 | msgid "修改后的字段数量与类型属性不对应。" 475 | msgstr "" 476 | 477 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 478 | msgid "生成表格失败:" 479 | msgstr "" 480 | 481 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 482 | msgid "表格名不能为空" 483 | msgstr "" 484 | 485 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 486 | msgid "非法脚本文件" 487 | msgstr "" 488 | 489 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 490 | msgid "非法内部类" 491 | msgstr "" 492 | 493 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 494 | msgid "原生类不存在需要的静态实例化方法" 495 | msgstr "" 496 | 497 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 498 | msgid "表格工具脚本不存在: " 499 | msgstr "" 500 | 501 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 502 | msgid "表格工具无法被实例化: " 503 | msgstr "" 504 | 505 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 506 | msgid "脚本不是继承自合法的表格工具: " 507 | msgstr "" 508 | 509 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 510 | msgid "请查阅: " 511 | msgstr "" 512 | 513 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 514 | msgid "表格工具不支持该扩展名: " 515 | msgstr "" 516 | 517 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 518 | msgid "重复的附加字段将被跳过:" 519 | msgstr "" 520 | 521 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 522 | msgid "被生成的属性中缺少需要的实例化参数: " 523 | msgstr "" 524 | 525 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 526 | msgid "不存在的优先排序字段将被跳过: " 527 | msgstr "" 528 | 529 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 530 | msgid "无法创建备份路径: " 531 | msgstr "" 532 | 533 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 534 | msgid "无法创建备份: " 535 | msgstr "" 536 | 537 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 538 | msgid "指定的表格工具无法解析已有的表格: " 539 | msgstr "" 540 | 541 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 542 | msgid "无效的生成修改器脚本: " 543 | msgstr "" 544 | 545 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 546 | msgid "生成修改器无法被实例化: " 547 | msgstr "" 548 | 549 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 550 | msgid "脚本不是继承自合法的合法的生成修改器: " 551 | msgstr "" 552 | 553 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 554 | msgid "不被导入" 555 | msgstr "" 556 | 557 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 558 | msgid "生成表格成功:" 559 | msgstr "" 560 | 561 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 562 | msgid "导入失败:" 563 | msgstr "" 564 | 565 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 566 | msgid "表格工具脚本不存在:" 567 | msgstr "" 568 | 569 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 570 | msgid "表格工具无法被实例化:" 571 | msgstr "" 572 | 573 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 574 | msgid "脚本不是继承自合法的表格工具:" 575 | msgstr "" 576 | 577 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 578 | msgid "表格工具不支持该扩展名:" 579 | msgstr "" 580 | 581 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 582 | msgid "表格不存在:" 583 | msgstr "" 584 | 585 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 586 | msgid "指定表格工具无法解析表格:" 587 | msgstr "" 588 | 589 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 590 | msgid "指定导入工具不存在:" 591 | msgstr "" 592 | 593 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 594 | msgid "指定导入工具无法实例化:" 595 | msgstr "" 596 | 597 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 598 | msgid "导入表格失败:" 599 | msgstr "" 600 | 601 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 602 | msgid "无效的导入修改器脚本: " 603 | msgstr "" 604 | 605 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 606 | msgid "导入修改器无法被实例化: " 607 | msgstr "" 608 | 609 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 610 | msgid "脚本不是继承自合法的合法的导入修改器: " 611 | msgstr "" 612 | 613 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 614 | msgid "类无法被实例化" 615 | msgstr "" 616 | 617 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 618 | msgid "脚本类不存在需要的静态实例化方法" 619 | msgstr "" 620 | 621 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 622 | msgid "原生类不存在或者不能被实例化" 623 | msgstr "" 624 | 625 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 626 | msgid "指定脚本不是继承自导入工具:" 627 | msgstr "" 628 | 629 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 630 | msgid "导入工具不支持该扩展名:" 631 | msgstr "" 632 | 633 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 634 | msgid "导入失败,无法创建导入路径:" 635 | msgstr "" 636 | 637 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 638 | msgid "导入表格成功:" 639 | msgstr "" 640 | 641 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 642 | msgid "不支持的属性类型将被跳过:" 643 | msgstr "" 644 | 645 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 646 | msgid "如果是脚本类型可以安全忽略。" 647 | msgstr "" 648 | 649 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 650 | msgid "非法属性名称: " 651 | msgstr "" 652 | 653 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 654 | msgid "非法属性类型: " 655 | msgstr "" 656 | 657 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 658 | msgid "无法读取csv文件: " 659 | msgstr "" 660 | 661 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 662 | msgid "解析csv文件失败: " 663 | msgstr "" 664 | 665 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 666 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 667 | msgid "请使用生成工具创建合法的表头。" 668 | msgstr "" 669 | 670 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 671 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 672 | msgid "非法标识符: " 673 | msgstr "" 674 | 675 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 676 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 677 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 678 | msgid "不支持的类型: " 679 | msgstr "" 680 | 681 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 682 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 683 | msgid "生成表格失败: " 684 | msgstr "" 685 | 686 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 687 | msgid "生成表格失败,无法生成:" 688 | msgstr "" 689 | 690 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 691 | msgid "转换为文本失败,不支持的类型: " 692 | msgstr "" 693 | 694 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 695 | msgid "Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。" 696 | msgstr "" 697 | 698 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 699 | msgid "非法值文本: " 700 | msgstr "" 701 | 702 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 703 | msgid "无法转换,类型与字段不符" 704 | msgstr "" 705 | 706 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 707 | msgid "无法转换,不支持的类型: " 708 | msgstr "" 709 | 710 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 711 | msgid "解析xlsx文件: " 712 | msgstr "" 713 | 714 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 715 | msgid "必须使用 sheet=your_sheet_name 选项指定工作表。" 716 | msgstr "" 717 | 718 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 719 | msgid "无法读取xlsx文件: " 720 | msgstr "" 721 | 722 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 723 | msgid "无法解析xlsx文件: " 724 | msgstr "" 725 | 726 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 727 | msgid "不存在指定的工作表: " 728 | msgstr "" 729 | 730 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 731 | msgid "非法格式" 732 | msgstr "" 733 | 734 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 735 | msgid "解析xlsx文件失败: " 736 | msgstr "" 737 | 738 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 739 | msgid "无法覆盖xlsx文件: " 740 | msgstr "" 741 | 742 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/scripts/preset.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | 4 | const _Log = preload("log.gd") 5 | const _TableHeader = preload("table_header.gd") 6 | const _TableTool = preload("../table_tools/table_tool.gd") 7 | const _ImportTool = preload("../import_tools/import_tool.gd") 8 | const _GenerateModifier = preload("generate_modifier.gd") 9 | const _ImportModifier = preload("import_modifier.gd") 10 | const _Localize = preload("../localization/localize.gd") 11 | 12 | const _DEFAULT_GENERATE_MODIFIER_FILE := "res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd" 13 | const _DEFAULT_IMPORT_MODIFIER_FILE := "res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd" 14 | 15 | @export_group("Editor Tool") 16 | # 触发生成 17 | @export var trigger_generate_table: bool = false: 18 | set(v): 19 | if v: 20 | generate_table() 21 | # 触发导入 22 | @export var trigger_import_table: bool = false: 23 | set(v): 24 | if v: 25 | import_table() 26 | 27 | @export_group("Basic") 28 | @export var name: String 29 | # 数据类 30 | @export var data_class: String 31 | var data_class_script: String: 32 | set(v): 33 | _data_class_script = _get_script_by_path(v) 34 | get: 35 | return _get_script_file_path(_data_class_script) 36 | @export var _data_class_script: Script # 为保兼容性,仅作为保存用的脚本引用 37 | @export var table_name: String 38 | 39 | # 表格生成选项 40 | @export var skip_prefix_underscore_properties: bool = false 41 | @export var only_storage_properties: bool = false 42 | @export var no_inheritance: bool = true 43 | @export var ascending_order: bool = false 44 | @export var auto_backup: bool = false # 45 | @export var auto_merge: bool = true # 46 | @export var priority_properties: PackedStringArray: 47 | get: 48 | return _strip_str_arr_elements_edges(priority_properties) 49 | @export var ignored_properties: PackedStringArray: 50 | get: 51 | return _strip_str_arr_elements_edges(ignored_properties) 52 | 53 | @export_group("Table") 54 | @export var table_tool_options: PackedStringArray: 55 | get: 56 | return _strip_str_arr_elements_edges(table_tool_options) 57 | # Deprecated 58 | var table_tool_script_file: String: 59 | set(v): 60 | table_tool_script = _get_script_by_path(v) 61 | get: 62 | return _get_script_file_path(table_tool_script) 63 | @export var table_tool_script: Script = preload("res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd") 64 | @export_file() var table_output_path: String = "res://tables/{table_name}.csv" 65 | # Deprecated 66 | var generate_modifier_file: String: 67 | set(v): 68 | generate_modifier = _get_script_by_path(v) 69 | get: 70 | return _get_script_file_path(generate_modifier) 71 | @export var generate_modifier: Script = preload(_DEFAULT_GENERATE_MODIFIER_FILE) 72 | 73 | @export_group("Import") 74 | # 表格导入选项 75 | @export var instantiation: String 76 | @export var import_tool_options: PackedStringArray: 77 | get: 78 | return _strip_str_arr_elements_edges(import_tool_options) 79 | var import_tool_script_file: String: 80 | set(v): 81 | import_tool_script = _get_script_by_path(v) 82 | get: 83 | return _get_script_file_path(import_tool_script) 84 | @export var import_tool_script: Script = preload("res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd") 85 | @export_file() var import_path: String = "res://tables/imported/{table_name}.gd" 86 | # Deprecated 87 | var import_modifier_file: String: 88 | set(v): 89 | import_modifier = _get_script_by_path(v) 90 | get: 91 | return _get_script_file_path(import_modifier) 92 | @export var import_modifier: Script = preload(_DEFAULT_IMPORT_MODIFIER_FILE) 93 | 94 | @export_group("Addtional") 95 | # 附加 96 | @export var additional_properties: Array[Dictionary] 97 | @export var descriptions: Dictionary 98 | @export var meta_list: PackedStringArray 99 | # 需要meta的字段,生成时将在对应的字段下一列插入一列#开头的字段,仅用于编辑时,不会被导入 100 | @export var need_meta_properties: PackedStringArray: 101 | get: 102 | return _strip_str_arr_elements_edges(need_meta_properties) 103 | 104 | #region 旧版兼容 105 | func _set(property: StringName, value: Variant) -> bool: 106 | match property: 107 | &"only_strage_properties": 108 | only_storage_properties = value 109 | &"table_ouput_path": 110 | table_output_path = value 111 | &"metas": 112 | meta_list = value 113 | _: 114 | return false 115 | return true 116 | 117 | 118 | func _get(property: StringName) -> Variant: 119 | match property: 120 | &"only_strage_properties": 121 | return only_storage_properties 122 | &"table_ouput_path": 123 | return table_output_path 124 | &"metas": 125 | return meta_list 126 | return null 127 | #endregion 旧版兼容 128 | 129 | 130 | ## enable_modifier: 是否启用修改器 131 | ## func_modify_data: Callable 修改要生成的数据行,参数为 Array[Dictionary], 返回修改后的数据 Array[Dictionary] 132 | func generate_table(enable_modifier: bool = true, func_modify_data: Callable = Callable()) -> Error: 133 | if table_name.is_empty(): 134 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格名不能为空")]) 135 | return ERR_INVALID_PARAMETER 136 | 137 | instantiation = instantiation.strip_edges() 138 | var instantiation_method := "new" if instantiation.is_empty() else instantiation.split("(")[0] 139 | var instantiation_args := Array([] if instantiation.is_empty() or instantiation.ends_with("()") else Array(instantiation.trim_suffix(")").split("(")).back().split(",", false)).map( 140 | func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}") 141 | ) as PackedStringArray 142 | 143 | var script: Script = _data_class_script 144 | var property_list: Array[Dictionary] 145 | 146 | if is_instance_valid(script): 147 | if not data_class.is_empty(): 148 | # 脚本内部类 149 | script = script[data_class] 150 | if not is_instance_valid(script): 151 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("非法内部类"), " - ", data_class]) 152 | return ERR_INVALID_PARAMETER 153 | 154 | _append_base_property_list_recursively_script(no_inheritance, script, property_list) 155 | else: 156 | if instantiation_method != "new": 157 | if not ClassDB.class_has_method(data_class, instantiation_method): 158 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在需要的静态实例化方法"), " - ", data_class, " - ", instantiation_method]) 159 | return ERR_INVALID_PARAMETER 160 | _append_base_property_list_recursively(no_inheritance, data_class, property_list) 161 | 162 | # 排除忽略的属性 163 | property_list = property_list.filter(func(p: Dictionary) -> bool: return not p["name"] in ignored_properties) 164 | 165 | # 附加属性检查 166 | for ap in additional_properties: 167 | if not _is_ap_valid(ap): 168 | return ERR_INVALID_PARAMETER 169 | 170 | if skip_prefix_underscore_properties: 171 | property_list = property_list.filter(func(p: Dictionary) -> bool: return not p["name"].begins_with("_")) 172 | 173 | # 仅 PROPERTY_USAGE_STORAGE 174 | if only_storage_properties: 175 | property_list = property_list.filter(func(p: Dictionary) -> bool: return p["usage"] & PROPERTY_USAGE_STORAGE) 176 | 177 | # 表格工具实例化 178 | if not is_instance_valid(table_tool_script): 179 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具脚本不存在: "), table_tool_script.resource_path]) 180 | return ERR_INVALID_PARAMETER 181 | 182 | if table_tool_script.reload() != OK: 183 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具脚本不存在: "), table_tool_script.resource_path]) 184 | return ERR_INVALID_PARAMETER 185 | 186 | if not table_tool_script.can_instantiate(): 187 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具无法被实例化: "), table_tool_script.resource_path]) 188 | return ERR_INVALID_PARAMETER 189 | 190 | var table_tool := table_tool_script.new() as _TableTool 191 | if not is_instance_valid(table_tool): 192 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本不是继承自合法的表格工具: "), table_tool_script.resource_path]) 193 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd"]) 194 | return ERR_INVALID_PARAMETER 195 | 196 | var table_file := table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower()) 197 | 198 | if table_tool.get_table_file_extension().to_lower() != table_file.get_extension().to_lower(): 199 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具不支持该扩展名: "), table_output_path]) 200 | return ERR_INVALID_PARAMETER 201 | 202 | # 过滤不支持的类型 203 | property_list = property_list.filter(_exclude_unsupported_type_filter.bind(table_tool.get_support_types())) 204 | 205 | # 添加附加属性 206 | for ap in additional_properties: 207 | var ap_name: String = ap.name.strip_edges() 208 | 209 | if property_list.filter(func(p: Dictionary) -> bool: return p["name"] == ap_name).size(): 210 | _Log.warning([name, " - ", _Localize.translate("重复的附加字段将被跳过:"), ap_name]) 211 | continue 212 | 213 | property_list.append({"name": ap_name, "type": ap.type}) 214 | 215 | # 检查实例化需要的参数 216 | for arg in instantiation_args: 217 | if property_list.filter(func(p: Dictionary) -> bool: return p["name"] == arg).is_empty(): 218 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("被生成的属性中缺少需要的实例化参数: "), arg]) 219 | return ERR_INVALID_PARAMETER 220 | 221 | # 升序 222 | if ascending_order: 223 | property_list.sort_custom(_sort_ascending) 224 | 225 | # 优先排序 226 | for i in range(priority_properties.size() - 1, -1, -1): 227 | var prop_name := priority_properties[i].strip_edges() 228 | var filtered := property_list.filter(func(p: Dictionary) -> bool: return p["name"] == prop_name) 229 | if filtered.size() <= 0: 230 | _Log.warning([name, " - ", _Localize.translate("不存在的优先排序字段将被跳过: "), prop_name]) 231 | continue 232 | # 将其调到最前端 233 | var first := filtered.front() as Dictionary 234 | property_list.erase(first) 235 | property_list.push_front(first) 236 | 237 | # 将meta字段插入 238 | var need_meta_props := Array(need_meta_properties).map(func(e: String) -> String: return e.strip_edges()) as PackedStringArray 239 | if need_meta_props.size(): 240 | var idx := 0 241 | while idx < property_list.size(): 242 | var p := property_list[idx] as Dictionary 243 | if need_meta_props.has(p["name"]): 244 | idx += 1 245 | property_list.insert(idx, {"name": "#" + p["name"], "type": TYPE_STRING}) 246 | idx += 1 247 | 248 | var backup_file := _converted_to_backup_file_path(table_file) 249 | 250 | var data: Array[Dictionary] = [] 251 | if FileAccess.file_exists(table_file): 252 | if not DirAccess.dir_exists_absolute(backup_file.get_base_dir()): 253 | var make_dir_err := DirAccess.make_dir_recursive_absolute(backup_file.get_base_dir()) 254 | if make_dir_err != OK: 255 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无法创建备份路径: "), backup_file.get_base_dir()]) 256 | return make_dir_err 257 | 258 | var copy_err := DirAccess.copy_absolute(table_file, backup_file) 259 | if copy_err != OK: 260 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无法创建备份: "), backup_file]) 261 | return copy_err 262 | 263 | # 自动合并 264 | if auto_merge: 265 | var parse_err := table_tool.parse_table_file(table_file, table_tool_options) 266 | if parse_err != OK: 267 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("指定的表格工具无法解析已有的表格: "), table_file]) 268 | return parse_err 269 | data = table_tool.get_data().duplicate(true) 270 | 271 | # 修改器 272 | var modified_fields := property_list.map(func(p: Dictionary) -> String: return p["name"]) as PackedStringArray 273 | var modified_types := property_list.map(func(p: Dictionary) -> int: return p["type"]) as PackedByteArray 274 | var modified_data := data 275 | var modified_options := table_tool_options.duplicate() 276 | var modified_meta_list := meta_list.duplicate() 277 | var modified_desc := descriptions.duplicate() 278 | if enable_modifier: 279 | if not is_instance_valid(generate_modifier): 280 | generate_modifier = ResourceLoader.load(_DEFAULT_GENERATE_MODIFIER_FILE, "Script", ResourceLoader.CACHE_MODE_IGNORE) 281 | 282 | if generate_modifier.reload(false) != OK: 283 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无效的生成修改器脚本: "), generate_modifier.resource_path]) 284 | return ERR_INVALID_PARAMETER 285 | 286 | if not generate_modifier.can_instantiate(): 287 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("生成修改器无法被实例化: "), generate_modifier.resource_path]) 288 | return ERR_INVALID_PARAMETER 289 | 290 | var modifier := generate_modifier.new() as _GenerateModifier 291 | if not is_instance_valid(modifier): 292 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本不是继承自合法的合法的生成修改器: "), generate_modifier.resource_path]) 293 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"]) 294 | return ERR_INVALID_PARAMETER 295 | 296 | # 修改 297 | modifier.begin_modify(table_name, data_class.strip_edges(), data_class_script.strip_edges()) 298 | modifier.modify_fields_definitions(modified_fields, modified_types) 299 | modified_data = modifier.modify_data(modified_data) 300 | modified_desc = modifier.modify_descriptions(modified_desc) 301 | modified_meta_list = modifier.modify_meta_list(modified_meta_list) 302 | modified_options = modifier.modify_table_tool_options(modified_options) 303 | 304 | # 通过数据修改方法修改数据 305 | if func_modify_data.is_valid(): 306 | modified_data = func_modify_data.call(modified_data) 307 | 308 | var data_rows: Array[PackedStringArray] 309 | if not modified_data.is_empty(): 310 | data_rows = table_tool.to_data_rows(modified_data, modified_fields, modified_types) 311 | 312 | # 准备表头 313 | var header := _TableHeader.new() 314 | header.meta_list = modified_meta_list 315 | header.fields = modified_fields 316 | header.types = Array(modified_types).map(func(t: int) -> String: return type_string(t)) as PackedStringArray 317 | header.descriptions.resize(header.fields.size()) 318 | for i in range(header.fields.size()): 319 | var f := header.fields[i] 320 | if f.begins_with("#"): 321 | header.descriptions[i] = _Localize.translate("不被导入") 322 | elif modified_desc.has(f): 323 | header.descriptions[i] = modified_desc[f] 324 | 325 | var err := table_tool.generate_table_file(table_file, header, data_rows, table_tool_options) 326 | if not auto_backup: 327 | # 不需要备份,删除 328 | DirAccess.remove_absolute(backup_file) 329 | 330 | if err != OK: 331 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), error_string(err)]) 332 | return err 333 | 334 | _Log.info([name, " - ", _Localize.translate("生成表格成功:"), table_file]) 335 | _update_file_change(table_file) 336 | return OK 337 | 338 | 339 | ## enable_modifier: 是否启用修改器 340 | func import_table(enable_modifier: bool = true) -> Error: 341 | # 表格工具 342 | if not is_instance_valid(table_tool_script): 343 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具脚本不存在:"), table_tool_script.resource_path]) 344 | return ERR_INVALID_PARAMETER 345 | 346 | if table_tool_script.reload(false) != OK: 347 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具脚本不存在:"), table_tool_script.resource_path]) 348 | return ERR_INVALID_PARAMETER 349 | 350 | if not table_tool_script.can_instantiate(): 351 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具无法被实例化:"), table_tool_script.resource_path]) 352 | return ERR_INVALID_PARAMETER 353 | 354 | var table_tool := table_tool_script.new() as _TableTool 355 | if not is_instance_valid(table_tool): 356 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("脚本不是继承自合法的表格工具:"), table_tool_script.resource_path]) 357 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd"]) 358 | return ERR_INVALID_PARAMETER 359 | 360 | var table_file := table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower()) 361 | 362 | if table_tool.get_table_file_extension().to_lower() != table_file.get_extension().to_lower(): 363 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具不支持该扩展名:"), table_output_path]) 364 | return ERR_INVALID_PARAMETER 365 | if not FileAccess.file_exists(table_file): 366 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格不存在:"), table_file]) 367 | return ERR_FILE_NOT_FOUND 368 | 369 | var err := table_tool.parse_table_file(table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower()), table_tool_options) 370 | if err != OK: 371 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定表格工具无法解析表格:"), error_string(err)]) 372 | return err 373 | 374 | # 导入工具 375 | if not is_instance_valid(import_tool_script): 376 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定导入工具不存在:"), import_tool_script.resource_path]) 377 | return ERR_INVALID_PARAMETER 378 | 379 | if not import_tool_script.can_instantiate(): 380 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定导入工具无法实例化:"), import_tool_script.resource_path]) 381 | return ERR_INVALID_PARAMETER 382 | 383 | var header := table_tool.get_header() 384 | if not is_instance_valid(header): 385 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定表格工具无法解析表格:"), error_string(ERR_PARSE_ERROR)]) 386 | return ERR_PARSE_ERROR 387 | 388 | var custom_setters := {} 389 | for ap in additional_properties: 390 | if not ap.setter.is_empty(): 391 | custom_setters[ap.name] = ap.setter 392 | 393 | # 修改器 394 | var modified_table_name := table_name 395 | var modified_custom_setter := custom_setters 396 | var modified_data := table_tool.get_data().duplicate(true) 397 | var modified_options := import_tool_options.duplicate() 398 | if enable_modifier: 399 | if not is_instance_valid(import_modifier): 400 | import_modifier = ResourceLoader.load(_DEFAULT_IMPORT_MODIFIER_FILE, "Script", ResourceLoader.CACHE_MODE_IGNORE) 401 | 402 | if import_modifier.reload(false) != OK: 403 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("无效的导入修改器脚本: "), import_modifier.resource_path]) 404 | return ERR_INVALID_PARAMETER 405 | 406 | if not import_modifier.can_instantiate(): 407 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("导入修改器无法被实例化: "), import_modifier.resource_path]) 408 | return ERR_INVALID_PARAMETER 409 | 410 | var modifier := import_modifier.new() as _ImportModifier 411 | if not is_instance_valid(modifier): 412 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("脚本不是继承自合法的合法的导入修改器: "), import_modifier.resource_path]) 413 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"]) 414 | return ERR_INVALID_PARAMETER 415 | 416 | # 修改 417 | modifier.begin_modify(table_name, data_class.strip_edges(), data_class_script.strip_edges()) 418 | modified_table_name = modifier.modify_table_name(modified_table_name) 419 | modified_custom_setter = modifier.modify_custom_setters(modified_custom_setter) 420 | modified_data = modifier.modify_data(modified_data) 421 | header.meta_list = modifier.modify_meta_list(header.meta_list) 422 | modified_options = modifier.modify_import_tool_options(modified_options) 423 | 424 | var inst := instantiation.strip_edges() 425 | if inst.is_empty(): 426 | inst = "new()" 427 | 428 | var script: Script = _data_class_script 429 | if is_instance_valid(script): 430 | if not data_class.is_empty(): 431 | # 脚本内部类 432 | script = script[data_class] 433 | if not is_instance_valid(script): 434 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("非法内部类"), " - ", data_class]) 435 | return ERR_INVALID_PARAMETER 436 | 437 | if not Engine.is_editor_hint() and not script.can_instantiate(): 438 | # 只在非编辑器下进行检查 439 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("类无法被实例化"), " - ", data_class_script, " - ", data_class]) 440 | return ERR_INVALID_PARAMETER 441 | 442 | if not inst.begins_with("new"): 443 | if script.get_script_method_list().filter(func(m: Dictionary) -> bool: return m["name"] == inst.split("(")[0] and m["flags"] & METHOD_FLAG_STATIC).is_empty(): 444 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本类不存在需要的静态实例化方法"), " - ", data_class_script, " - ", inst]) 445 | return ERR_INVALID_PARAMETER 446 | else: 447 | if not ClassDB.class_exists(data_class) or not ClassDB.can_instantiate(data_class): 448 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在或者不能被实例化"), " - ", data_class]) 449 | return ERR_INVALID_PARAMETER 450 | 451 | if not inst.begins_with("new"): 452 | if not ClassDB.class_has_method(data_class, inst.split("(")[0]): 453 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在需要的静态实例化方法"), " - ", data_class, " - ", inst]) 454 | return ERR_INVALID_PARAMETER 455 | 456 | var import_tool := import_tool_script.new() as _ImportTool 457 | if not is_instance_valid(import_tool): 458 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定脚本不是继承自导入工具:"), import_tool_script.resource_path]) 459 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd"]) 460 | return ERR_INVALID_PARAMETER 461 | 462 | var import_file_path := import_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower()) 463 | if import_tool.get_import_file_extension().to_lower() != import_file_path.get_extension().to_lower(): 464 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("导入工具不支持该扩展名:"), import_file_path]) 465 | return ERR_INVALID_PARAMETER 466 | 467 | if not DirAccess.dir_exists_absolute(import_file_path.get_base_dir()): 468 | err = DirAccess.make_dir_recursive_absolute(import_file_path.get_base_dir()) 469 | if err != OK: 470 | _Log.error([name, " - ", _Localize.translate("导入失败,无法创建导入路径:"), import_file_path.get_base_dir(), " - ", error_string(err)]) 471 | return err 472 | 473 | err = import_tool.import(import_file_path, modified_table_name, header, data_class, data_class_script, inst, modified_custom_setter, modified_data, modified_options) 474 | if err != OK: 475 | _Log.error([name, " - ", _Localize.translate("导入失败:"), error_string(err)]) 476 | return err 477 | 478 | _Log.info([name, " - ", _Localize.translate("导入表格成功:"), import_file_path]) 479 | _update_file_change(import_file_path) 480 | return OK 481 | 482 | 483 | #--------------------- 484 | func _get_script_by_path(path: String) -> Script: 485 | if FileAccess.file_exists(path) and ResourceLoader.exists(path, &"Script"): 486 | return load(path) 487 | return null 488 | 489 | 490 | func _get_script_file_path(script: Script) -> String: 491 | if is_instance_valid(script): 492 | return script.resource_path 493 | return "" 494 | 495 | 496 | func _strip_str_arr_elements_edges(str_arr: PackedStringArray) -> PackedStringArray: 497 | for i in range(str_arr.size()): 498 | str_arr[i] = str_arr[i].strip_edges() 499 | return str_arr 500 | 501 | 502 | func _update_file_change(file: String) -> void: 503 | if not Engine.is_editor_hint(): 504 | return 505 | EditorInterface.get_resource_filesystem().update_file(file) 506 | var file_type := EditorInterface.get_resource_filesystem().get_file_type(file) 507 | if ClassDB.is_parent_class(file_type, &"Script"): 508 | if Engine.get_main_loop().has_meta(&"__HACK_TIMER__"): 509 | return 510 | var timer := (Engine.get_main_loop() as SceneTree).create_timer(1.0) 511 | timer.timeout.connect(func() -> void: 512 | # Hack 513 | EditorInterface.get_script_editor().notification(Node.NOTIFICATION_APPLICATION_FOCUS_IN) 514 | Engine.get_main_loop().remove_meta(&"__HACK_TIMER__") 515 | ) 516 | Engine.get_main_loop().set_meta(&"__HACK_TIMER__", timer) 517 | elif file_type == &"Resource" or ClassDB.is_parent_class(file_type, &"Resource"): 518 | EditorInterface.get_resource_filesystem().reimport_files([file]) 519 | 520 | 521 | func _converted_to_backup_file_path(origin_path: String) -> String: 522 | var base_dir := origin_path.get_base_dir().path_join(".backup") 523 | var prefix := ".%s_" % (Time.get_datetime_string_from_system().replace(":", "-") + str(Time.get_unix_time_from_system())) 524 | return base_dir.path_join(prefix + origin_path.get_file()) 525 | 526 | 527 | func _get_custom_setter(prop_name: String) -> String: 528 | var filtered := additional_properties.filter(func(ap: Dictionary) -> bool: return ap.get("name", "").strip_edges() == prop_name) 529 | if filtered.size(): 530 | return filtered.front().get("setter", "") 531 | return "" 532 | 533 | 534 | func _exclude_unsupported_type_filter(p: Dictionary, support_types: PackedByteArray) -> bool: 535 | var t := p["type"] as int 536 | if not t in support_types: 537 | _Log.warning([name, " - ", _Localize.translate("不支持的属性类型将被跳过:"), p["name"], " - ", type_string(t), " ", _Localize.translate("如果是脚本类型可以安全忽略。")]) 538 | return false 539 | return true 540 | 541 | 542 | func _sort_ascending(a: Dictionary, b: Dictionary) -> bool: 543 | var an := a["name"] as String 544 | var bn := b["name"] as String 545 | return an > bn 546 | 547 | 548 | func _append_base_property_list_recursively_script(p_no_inheritance: bool, script: Script, r_property_list: Array[Dictionary]) -> void: 549 | if not is_instance_valid(script): 550 | return 551 | r_property_list.append_array(script.get_script_property_list()) 552 | 553 | if p_no_inheritance: 554 | return 555 | 556 | var base := script.get_base_script() 557 | if is_instance_valid(base): 558 | _append_base_property_list_recursively_script(p_no_inheritance, script, r_property_list) 559 | else: 560 | _append_base_property_list_recursively(p_no_inheritance, script.get_instance_base_type(), r_property_list) 561 | 562 | 563 | func _append_base_property_list_recursively(p_no_inheritance: bool, klass: StringName, r_property_list: Array[Dictionary]) -> void: 564 | if klass.is_empty(): 565 | return 566 | 567 | if klass in [&"Object", &"RefCounted", &"Resource", &"Node"]: 568 | # 跳过基础类 569 | return 570 | 571 | r_property_list.append_array(ClassDB.class_get_property_list(klass, true)) 572 | 573 | if p_no_inheritance: 574 | return 575 | 576 | _append_base_property_list_recursively(p_no_inheritance, ClassDB.get_parent_class(klass), r_property_list) 577 | 578 | 579 | func _is_ap_valid(ap: Dictionary) -> bool: 580 | ap["name"] = ap.get("name", "").strip_edges() 581 | if not TextServerManager.get_primary_interface().is_valid_identifier(ap["name"]): 582 | _Log.error([_Localize.translate("非法属性名称: "), ap["name"]]) 583 | return false 584 | 585 | var type := ap["type"] as int 586 | if type in [TYPE_NIL, TYPE_OBJECT, TYPE_CALLABLE, TYPE_SIGNAL, TYPE_RID]: 587 | _Log.error([_Localize.translate("非法属性类型: "), name, " - ", type_string(type)]) 588 | return false 589 | 590 | return true 591 | -------------------------------------------------------------------------------- /addons/config_table_manager.daylily-zeleen/localization/en.po: -------------------------------------------------------------------------------- 1 | # LANGUAGE translation for Config Table Manager for the following files: 2 | # res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 3 | # res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd 4 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd 5 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 6 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 7 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 8 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 9 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 10 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd 11 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 12 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd 13 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 14 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd 15 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 16 | # res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd 17 | # res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd 18 | # res://addons/config_table_manager.daylily-zeleen/scripts/log.gd 19 | # res://addons/config_table_manager.daylily-zeleen/scripts/preset.gd 20 | # res://addons/config_table_manager.daylily-zeleen/scripts/table_header.gd 21 | # res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 22 | # res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 23 | # res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 24 | # res://addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd 25 | # 26 | # FIRST AUTHOR , YEAR. 27 | # 28 | #, fuzzy 29 | msgid "" 30 | msgstr "" 31 | "Project-Id-Version: Config Table Manager\n" 32 | "MIME-Version: 1.0\n" 33 | "Content-Type: text/plain; charset=UTF-8\n" 34 | "Content-Transfer-Encoding: 8-bit\n" 35 | 36 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 37 | msgid "导表失败: " 38 | msgstr "Import table failed: " 39 | 40 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 41 | msgid "无法被赋值的字段将被跳过: " 42 | msgstr "An unassignable filed will be skiped: " 43 | 44 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd 45 | msgid "转换失败,不支持的类型: " 46 | msgstr "Unsupported type is converted failed: " 47 | 48 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 49 | msgid "未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。" 50 | msgstr "Has not specify the key of data class. Please use \"key=prop_name\" to specify as import option." 51 | 52 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 53 | msgid "数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。" 54 | msgstr "The data class has not this property (%s) to be the key. Please use \"key=prop_name\" to specify as import option." 55 | 56 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 57 | msgid "存在未配置key (%s)的数据: %s。" 58 | msgstr "Has a data without config key (%s): %s" 59 | 60 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd 61 | msgid "存在key (%s)重复的数据,重复的值为: %s。" 62 | msgstr "Duplicate key (%s), the value is: %s" 63 | 64 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 65 | msgid "要附加的属性名称" 66 | msgstr "The name of additional property" 67 | 68 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 69 | msgid "非空将使用自定义Setter进行设置" 70 | msgstr "Custom setter for assigning value. Keep empty will use \"=\" directly." 71 | 72 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn 73 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 74 | msgid " - " 75 | msgstr " - " 76 | 77 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 78 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 79 | msgid "选中" 80 | msgstr "Select" 81 | 82 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 83 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 84 | msgid "排除" 85 | msgstr "Exclude" 86 | 87 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 88 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 89 | msgid "预设" 90 | msgstr "Preset" 91 | 92 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd 93 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 94 | msgid "表格预设不存在: " 95 | msgstr "A non-existent table preset: " 96 | 97 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 98 | msgid "表格生成" 99 | msgstr "Table Generate" 100 | 101 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 102 | msgid "对选中项进行生成" 103 | msgstr "Generate Selected" 104 | 105 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 106 | msgid "对所有项进行生成" 107 | msgstr "Generate All" 108 | 109 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 110 | msgid "对选中项执行导入" 111 | msgstr "Import Selected" 112 | 113 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn 114 | msgid "对所有项执行导入" 115 | msgstr "Import All" 116 | 117 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 118 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 119 | msgid "无效的预设保存路径\"{save_dir}\",必须是资源目录下的合法路径。" 120 | msgstr "Invalid presets save directory \"{save_dir}\", it require a valid directory of resource file system." 121 | 122 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 123 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 124 | msgid "预设名称不能为空。" 125 | msgstr "Prset name must not empty." 126 | 127 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 128 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 129 | msgid "预设名称\"{preset_name}\"无法作为文件名" 130 | msgstr "Preset name \"{preset_name}\" can't be a file name." 131 | 132 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 133 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 134 | msgid "创建预设路径\"{presets_dir}\"失败: " 135 | msgstr "Create presets directory \"{presets_dir}\" failed: " 136 | 137 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 138 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 139 | msgid "保存预设失败\"{file_name}\"失败: " 140 | msgstr "Save preset \"{file_name}\" failed: " 141 | 142 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 143 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 144 | msgid "保存预设\"{preset_name}\"成功: " 145 | msgstr "Save preset \"{preset_name}\" success: " 146 | 147 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 148 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 149 | msgid "删除失败,预设”{file_path}“不存在" 150 | msgstr "Delete failed, preset "{file_path}" is not exists." 151 | 152 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 153 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 154 | msgid "删除预设失败: " 155 | msgstr "Delete preset failed: " 156 | 157 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 158 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 159 | msgid "删除预设成功: " 160 | msgstr "Delete preset success: " 161 | 162 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 163 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 164 | msgid "重复的字段描述将被跳过: " 165 | msgstr "A duplicate filed description will be skiped: " 166 | 167 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 168 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 169 | msgid "Bug,请提交issue并提供复现步骤" 170 | msgstr "But, please submit an issue and provide reproduce stps." 171 | 172 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 173 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 174 | msgid "删除预设失败,未选中预设" 175 | msgstr "Delete preset failed, has not seleting preset." 176 | 177 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 178 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 179 | msgid "选择脚本" 180 | msgstr "Select Script" 181 | 182 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 183 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 184 | msgid "输出文件" 185 | msgstr "Output File" 186 | 187 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd 188 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 189 | msgid "- None -" 190 | msgstr "- None -" 191 | 192 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 193 | msgid "预设:" 194 | msgstr "Preset:" 195 | 196 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 197 | msgid "预设名称: " 198 | msgstr "Preset Name: " 199 | 200 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 201 | msgid "要保存的预设名称" 202 | msgstr "The name of preset." 203 | 204 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 205 | msgid "保存" 206 | msgstr "Save" 207 | 208 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 209 | msgid "删除" 210 | msgstr "Delete" 211 | 212 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 213 | msgid "数据类名:" 214 | msgstr "Data Class Name: " 215 | 216 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 217 | msgid "原生类名或脚本的内部类名,留空时脚本不能为空" 218 | msgstr "Native class name or internal class of a script. If has not selete a script, this options must be specified." 219 | 220 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 221 | msgid "数据类脚本:" 222 | msgstr "Data Class Script: " 223 | 224 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 225 | msgid "可选项" 226 | msgstr "Options" 227 | 228 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 229 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 230 | msgid "选择" 231 | msgstr "Select" 232 | 233 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 234 | msgid "表格名:" 235 | msgstr "Table Name: " 236 | 237 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 238 | msgid "建议使用大驼峰命名" 239 | msgstr "Recommend to use PascalCase." 240 | 241 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 242 | msgid "优先字段:" 243 | msgstr "Priority Fields:" 244 | 245 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 246 | msgid "用\",\"分隔,未列入的字段将排在其后。例:id, name, description" 247 | msgstr "Seperate by \",\". e.g. id, name, description" 248 | 249 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 250 | msgid "忽略字段:" 251 | msgstr "Ignore Fileds: " 252 | 253 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 254 | msgid "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data" 255 | msgstr "Seperate by \",\". e.g. internal_id, changeable_data" 256 | 257 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 258 | msgid "需要元字段的字段: " 259 | msgstr "Need Meta Fields:" 260 | 261 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 262 | msgid "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。" 263 | msgstr "Seperate by \",\". Generate meta fields will not be imported, only use for editing tables." 264 | 265 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 266 | msgid "跳过\"_\"开头的属性" 267 | msgstr "Skip properties starting with \"_\"" 268 | 269 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 270 | msgid "即含有PROPERTY_USAGE_STORAGE的属性" 271 | msgstr "Which have "PROPERTY_USAGE_STORAGE" flag." 272 | 273 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 274 | msgid "仅需要存储的属性" 275 | msgstr "Only Storage Properties" 276 | 277 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 278 | msgid "不含父类属性" 279 | msgstr "Exclude Parent Properties" 280 | 281 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 282 | msgid "按字母升序" 283 | msgstr "In Ascending Alphabetical Order" 284 | 285 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 286 | msgid "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。" 287 | msgstr "" 288 | 289 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 290 | msgid "覆盖前备份" 291 | msgstr "Backup Before Overwrite" 292 | 293 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 294 | msgid "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并" 295 | msgstr "" 296 | 297 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 298 | msgid "自动合并" 299 | msgstr "Auto Merge Data" 300 | 301 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 302 | msgid "表格生成路径:" 303 | msgstr "Table Generate Path:" 304 | 305 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 306 | msgid "表格选项参数:" 307 | msgstr "Table Tool Options: " 308 | 309 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 310 | msgid "特定于表格工具的选项参数,用\",\"分隔。" 311 | msgstr "Option parameters specific to table tool, separated by \",\"." 312 | 313 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 314 | msgid "表格工具:" 315 | msgstr "Table Tool: " 316 | 317 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 318 | msgid "表格生成修改器:" 319 | msgstr "Table Generate Modifier: " 320 | 321 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 322 | msgid "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd" 323 | msgstr "Modify data in table generate workflow. Should extends from \"res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd\"" 324 | 325 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 326 | msgid "数据对象实例化:" 327 | msgstr "Instantiation of Data Object: " 328 | 329 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 330 | msgid "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造" 331 | msgstr "e.g. create({id}, {name}). Default is non-parameters constuctor." 332 | 333 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 334 | msgid "导入路径:" 335 | msgstr "Import Path: " 336 | 337 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 338 | msgid "导入选项参数:" 339 | msgstr "Import Tool Options: " 340 | 341 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 342 | msgid "" 343 | "特定于导入工具的选项参数,用\",\"分隔。\n" 344 | "例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。" 345 | msgstr "Option parameters specific to import tool, separated by \",\"." 346 | 347 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 348 | msgid "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name" 349 | msgstr "Option parameters specific to import tool, separated by \",\". e.g. \"generate_class_name\" for Default GDScript Import." 350 | 351 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 352 | msgid "导入工具:" 353 | msgstr "Import Tool: " 354 | 355 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 356 | msgid "导入修改器:" 357 | msgstr "Import Modifier: " 358 | 359 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 360 | msgid "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd" 361 | msgstr "Modify data in table import workflow. Should extends from \"res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd\"" 362 | 363 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 364 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 365 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 366 | msgid "+" 367 | msgstr "+" 368 | 369 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 370 | msgid "确认操作?" 371 | msgstr "Confirm?" 372 | 373 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 374 | msgid "预设管理" 375 | msgstr "Presets" 376 | 377 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 378 | msgid "生成与导入" 379 | msgstr "Generat & Import" 380 | 381 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 382 | msgid "设置" 383 | msgstr "Settings" 384 | 385 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 386 | msgid "表格选项" 387 | msgstr "Table Options" 388 | 389 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 390 | msgid "导入选项" 391 | msgstr "Import Options" 392 | 393 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 394 | msgid "附加字段" 395 | msgstr "Additnial Fields" 396 | 397 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 398 | msgid "字段描述" 399 | msgstr "Fields Descriptions" 400 | 401 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 402 | msgid "自定义元数据" 403 | msgstr "Custom Meta Data" 404 | 405 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn 406 | msgid "表格导入" 407 | msgstr "Table Import" 408 | 409 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 410 | msgid "Meta" 411 | msgstr "Meta" 412 | 413 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn 414 | msgid "X" 415 | msgstr "X" 416 | 417 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 418 | msgid "字段名称" 419 | msgstr "Filed Name" 420 | 421 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn 422 | msgid "描述" 423 | msgstr "Description" 424 | 425 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 426 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 427 | msgid "表格工具" 428 | msgstr "Table Tool" 429 | 430 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 431 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 432 | msgid "脚本路径" 433 | msgstr "Script Path" 434 | 435 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 436 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 437 | msgid "导入工具" 438 | msgstr "Import Tool" 439 | 440 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd 441 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 442 | msgid "保存设置失败: " 443 | msgstr "Save Setting Failed: " 444 | 445 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 446 | msgid "预设保存路径:" 447 | msgstr "Presets Save Path: " 448 | 449 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 450 | msgid "保存设置" 451 | msgstr "Save Settings" 452 | 453 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 454 | msgid "Open a Directory" 455 | msgstr "" 456 | 457 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 458 | msgid "CSV(,分隔)" 459 | msgstr "CSV(\",\" delimiter)" 460 | 461 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 462 | msgid "Excel(xlsx)" 463 | msgstr "" 464 | 465 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 466 | msgid "GDScript(TypedArray风格)" 467 | msgstr "GDScript(TypedArray Style)" 468 | 469 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn 470 | msgid "GDScript(Dictionary风格)" 471 | msgstr "GDScript(Dictionary风格 Style)" 472 | 473 | #: addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd 474 | msgid "修改后的字段数量与类型属性不对应。" 475 | msgstr "The number of modified fields does not match the types." 476 | 477 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 478 | msgid "生成表格失败:" 479 | msgstr "Generate table failed: " 480 | 481 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 482 | msgid "表格名不能为空" 483 | msgstr "Table name can not be empty." 484 | 485 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 486 | msgid "非法脚本文件" 487 | msgstr "Invalid script file" 488 | 489 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 490 | msgid "非法内部类" 491 | msgstr "Invalid internal class" 492 | 493 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 494 | msgid "原生类不存在需要的静态实例化方法" 495 | msgstr "The native class has not the static instantiation method" 496 | 497 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 498 | msgid "表格工具脚本不存在: " 499 | msgstr "Table Tool is not exists: " 500 | 501 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 502 | msgid "表格工具无法被实例化: " 503 | msgstr "The table tool can't be instantiated: " 504 | 505 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 506 | msgid "脚本不是继承自合法的表格工具: " 507 | msgstr "The script is not a valid table tool: " 508 | 509 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 510 | msgid "请查阅: " 511 | msgstr "Please refer to: " 512 | 513 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 514 | msgid "表格工具不支持该扩展名: " 515 | msgstr "The table tool is unsupport this extension: " 516 | 517 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 518 | msgid "重复的附加字段将被跳过:" 519 | msgstr "Duplicate additional filed will be skiped: " 520 | 521 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 522 | msgid "被生成的属性中缺少需要的实例化参数: " 523 | msgstr "Missing the required instantiation parameter:" 524 | 525 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 526 | msgid "不存在的优先排序字段将被跳过: " 527 | msgstr "Priority sorting fields that do not exist will be skipped: " 528 | 529 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 530 | msgid "无法创建备份路径: " 531 | msgstr "Can not create backup directory: " 532 | 533 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 534 | msgid "无法创建备份: " 535 | msgstr "Can not create backup: " 536 | 537 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 538 | msgid "指定的表格工具无法解析已有的表格: " 539 | msgstr "The table tool can't parsing exists table: " 540 | 541 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 542 | msgid "无效的生成修改器脚本: " 543 | msgstr "Invalid generate modifier script: " 544 | 545 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 546 | msgid "生成修改器无法被实例化: " 547 | msgstr "Can't instantiate generate modifier: " 548 | 549 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 550 | msgid "脚本不是继承自合法的合法的生成修改器: " 551 | msgstr "The script is not a valid generate modifier: " 552 | 553 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 554 | msgid "不被导入" 555 | msgstr "Not imported." 556 | 557 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 558 | msgid "生成表格成功:" 559 | msgstr "Generate table success: " 560 | 561 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 562 | msgid "导入失败:" 563 | msgstr "Import table failed: " 564 | 565 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 566 | msgid "表格工具脚本不存在:" 567 | msgstr "Not exsits table tool script: " 568 | 569 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 570 | msgid "表格工具无法被实例化:" 571 | msgstr "Can't instantiate table tool: " 572 | 573 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 574 | msgid "脚本不是继承自合法的表格工具:" 575 | msgstr "The script is not a valid table tool: " 576 | 577 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 578 | msgid "表格工具不支持该扩展名:" 579 | msgstr "The table tool can't support this extension: " 580 | 581 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 582 | msgid "表格不存在:" 583 | msgstr "Table is not exists: " 584 | 585 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 586 | msgid "指定表格工具无法解析表格:" 587 | msgstr "The table tool can't parse table: " 588 | 589 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 590 | msgid "指定导入工具不存在:" 591 | msgstr "The import tool is not exists: " 592 | 593 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 594 | msgid "指定导入工具无法实例化:" 595 | msgstr "Can't not instantiate import tool: " 596 | 597 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 598 | msgid "导入表格失败:" 599 | msgstr "Import table failed: " 600 | 601 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 602 | msgid "无效的导入修改器脚本: " 603 | msgstr "The import modifier script is invalid: " 604 | 605 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 606 | msgid "导入修改器无法被实例化: " 607 | msgstr "Can't instantiate import modifier: " 608 | 609 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 610 | msgid "脚本不是继承自合法的合法的导入修改器: " 611 | msgstr "The script is not extends from a valid import modifer: " 612 | 613 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 614 | msgid "类无法被实例化" 615 | msgstr "Class can't be instantiated" 616 | 617 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 618 | msgid "脚本类不存在需要的静态实例化方法" 619 | msgstr "Script class has not the require static instantiation method" 620 | 621 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 622 | msgid "原生类不存在或者不能被实例化" 623 | msgstr "The native class is not exists or can't be instantiated." 624 | 625 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 626 | msgid "指定脚本不是继承自导入工具:" 627 | msgstr "The Script is not a valid import tool: " 628 | 629 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 630 | msgid "导入工具不支持该扩展名:" 631 | msgstr "The import tool is not support extension: " 632 | 633 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 634 | msgid "导入失败,无法创建导入路径:" 635 | msgstr "Import failed, can't create import directory." 636 | 637 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 638 | msgid "导入表格成功:" 639 | msgstr "Import table success: " 640 | 641 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 642 | msgid "不支持的属性类型将被跳过:" 643 | msgstr "Unsupport type will be skiped: " 644 | 645 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 646 | msgid "如果是脚本类型可以安全忽略。" 647 | msgstr "Can be ignore if it is script type." 648 | 649 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 650 | msgid "非法属性名称: " 651 | msgstr "Invlid property name: " 652 | 653 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd 654 | msgid "非法属性类型: " 655 | msgstr "Invalid property type: " 656 | 657 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 658 | msgid "无法读取csv文件: " 659 | msgstr "Can not read CSV file: " 660 | 661 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 662 | msgid "解析csv文件失败: " 663 | msgstr "Parse CSV failed: " 664 | 665 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 666 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 667 | msgid "请使用生成工具创建合法的表头。" 668 | msgstr "Please use table tool to generate a valid table header." 669 | 670 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 671 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 672 | msgid "非法标识符: " 673 | msgstr "Illegal identifier: " 674 | 675 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 676 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 677 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 678 | msgid "不支持的类型: " 679 | msgstr "Unsupport type: " 680 | 681 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 682 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 683 | msgid "生成表格失败: " 684 | msgstr "Generate table failed: " 685 | 686 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 687 | msgid "生成表格失败,无法生成:" 688 | msgstr "Generate table failed: " 689 | 690 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 691 | msgid "转换为文本失败,不支持的类型: " 692 | msgstr "Convert text failed, unsupport type: " 693 | 694 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 695 | msgid "Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。" 696 | msgstr "Bug: generate default value for empty value. Please submit an issue and provide reproduce steps or MRP." 697 | 698 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd 699 | msgid "非法值文本: " 700 | msgstr "Illegal value text: " 701 | 702 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 703 | msgid "无法转换,类型与字段不符" 704 | msgstr "Conver failed, types and files are not match" 705 | 706 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd 707 | msgid "无法转换,不支持的类型: " 708 | msgstr "Conver failed, unsupport type: " 709 | 710 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 711 | msgid "解析xlsx文件: " 712 | msgstr "Paese xlsx file: " 713 | 714 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 715 | msgid "必须使用 sheet=your_sheet_name 选项指定工作表。" 716 | msgstr "Must use "sheet=your_sheet_name" option to specific the worksheet." 717 | 718 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 719 | msgid "无法读取xlsx文件: " 720 | msgstr "Can't read xlsx file: " 721 | 722 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 723 | msgid "无法解析xlsx文件: " 724 | msgstr "Can't parse xlsx file: " 725 | 726 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 727 | msgid "不存在指定的工作表: " 728 | msgstr "The worksheet is not exists: " 729 | 730 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 731 | msgid "非法格式" 732 | msgstr "Illegal format" 733 | 734 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 735 | msgid "解析xlsx文件失败: " 736 | msgstr "Parse xlsx file failed: " 737 | 738 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd 739 | msgid "无法覆盖xlsx文件: " 740 | msgstr "Can't Overwrite xlsx file:" 741 | --------------------------------------------------------------------------------