└── addons └── pandora ├── api.gd ├── api.gd.uid ├── backend ├── entity_backend.gd └── entity_backend.gd.uid ├── context ├── context_manager.gd └── context_manager.gd.uid ├── icons ├── Array.svg ├── Array.svg.import ├── AssetLib.svg ├── AssetLib.svg.import ├── AtlasTexture.svg ├── AtlasTexture.svg.import ├── Clear.svg ├── Clear.svg.import ├── Color.svg ├── Color.svg.import ├── Edit.svg ├── Edit.svg.import ├── Folder.svg ├── Folder.svg.import ├── Font.svg.import ├── KeyValue.svg ├── KeyValue.svg.import ├── Object.svg ├── Object.svg.import ├── Reload.svg ├── Reload.svg.import ├── Remove.svg ├── Remove.svg.import ├── Save.svg ├── Save.svg.import ├── Search.svg ├── Search.svg.import ├── String.svg ├── String.svg.import ├── Vector2.svg ├── Vector2.svg.import ├── Vector2i.svg ├── Vector2i.svg.import ├── Vector3.svg ├── Vector3.svg.import ├── Vector3i.svg ├── Vector3i.svg.import ├── bool.svg ├── bool.svg.import ├── float.svg ├── float.svg.import ├── icon.png ├── icon.png.import ├── int.svg ├── int.svg.import ├── nanoid.svg ├── nanoid.svg.import ├── pandora-icon.svg ├── pandora-icon.svg.import ├── pandora-json-icon.svg └── pandora-json-icon.svg.import ├── model ├── category.gd ├── category.gd.uid ├── entity.gd ├── entity.gd.uid ├── property.gd ├── property.gd.uid ├── property_instance.gd ├── property_instance.gd.uid ├── reference.gd ├── reference.gd.uid ├── type.gd ├── type.gd.uid └── types │ ├── array.gd │ ├── array.gd.uid │ ├── bool.gd │ ├── bool.gd.uid │ ├── color.gd │ ├── color.gd.uid │ ├── float.gd │ ├── float.gd.uid │ ├── int.gd │ ├── int.gd.uid │ ├── reference.gd │ ├── reference.gd.uid │ ├── resource.gd │ ├── resource.gd.uid │ ├── string.gd │ ├── string.gd.uid │ ├── vector2.gd │ ├── vector2.gd.uid │ ├── vector2i.gd │ ├── vector2i.gd.uid │ ├── vector3.gd │ ├── vector3.gd.uid │ ├── vector3i.gd │ └── vector3i.gd.uid ├── plugin.cfg ├── plugin.gd ├── plugin.gd.uid ├── settings ├── pandora_settings.gd └── pandora_settings.gd.uid ├── storage ├── data_storage.gd ├── data_storage.gd.uid └── json │ ├── json_data_storage.gd │ └── json_data_storage.gd.uid ├── ui ├── components │ ├── array_editor │ │ ├── array_editor.gd │ │ ├── array_editor.gd.uid │ │ ├── array_editor.tscn │ │ ├── array_item.gd │ │ ├── array_item.gd.uid │ │ ├── array_item.tscn │ │ ├── array_manager.gd │ │ ├── array_manager.gd.uid │ │ ├── array_manager.tscn │ │ ├── array_window.gd │ │ └── array_window.gd.uid │ ├── color_picker │ │ ├── color_picker.gd │ │ ├── color_picker.gd.uid │ │ └── color_picker.tscn │ ├── entity_attributes │ │ ├── entity_attributes.gd │ │ ├── entity_attributes.gd.uid │ │ └── entity_attributes.tscn │ ├── entity_picker │ │ ├── entity_picker.gd │ │ ├── entity_picker.gd.uid │ │ └── entity_picker.tscn │ ├── entity_tree │ │ ├── entity_tree.gd │ │ ├── entity_tree.gd.uid │ │ └── entity_tree.tscn │ ├── loading_spinner │ │ ├── LoadingSpinner.tscn │ │ ├── loading_spinner.gd │ │ └── loading_spinner.gd.uid │ ├── notification_label │ │ ├── notification_label.gd │ │ ├── notification_label.gd.uid │ │ └── notification_label.tscn │ ├── progress_bar │ │ ├── progress_bar.gd │ │ ├── progress_bar.gd.uid │ │ └── progress_bar.tscn │ ├── properties │ │ ├── array │ │ │ ├── array_property.gd │ │ │ ├── array_property.gd.uid │ │ │ └── array_property.tscn │ │ ├── bool │ │ │ ├── bool_property.gd │ │ │ ├── bool_property.gd.uid │ │ │ └── bool_property.tscn │ │ ├── color │ │ │ ├── color_property.gd │ │ │ ├── color_property.gd.uid │ │ │ └── color_property.tscn │ │ ├── float │ │ │ ├── float_property.gd │ │ │ ├── float_property.gd.uid │ │ │ └── float_property.tscn │ │ ├── integer │ │ │ ├── integer_property.gd │ │ │ ├── integer_property.gd.uid │ │ │ └── integer_property.tscn │ │ ├── property_control.gd │ │ ├── property_control.gd.uid │ │ ├── property_control_kvp.gd │ │ ├── property_control_kvp.gd.uid │ │ ├── property_control_kvp.tscn │ │ ├── reference │ │ │ ├── reference_property.gd │ │ │ ├── reference_property.gd.uid │ │ │ └── reference_property.tscn │ │ ├── resource │ │ │ ├── resource_property.gd │ │ │ ├── resource_property.gd.uid │ │ │ └── resource_property.tscn │ │ ├── string │ │ │ ├── string_property.gd │ │ │ ├── string_property.gd.uid │ │ │ └── string_property.tscn │ │ └── vector │ │ │ ├── vector2 │ │ │ └── vector2_property.tscn │ │ │ ├── vector2i │ │ │ └── vector2i_property.tscn │ │ │ ├── vector3 │ │ │ └── vector3_property.tscn │ │ │ ├── vector3i │ │ │ └── vector3i_property.tscn │ │ │ ├── vector_property.gd │ │ │ └── vector_property.gd.uid │ ├── property_bar │ │ ├── property_bar.gd │ │ ├── property_bar.gd.uid │ │ ├── property_bar.tscn │ │ ├── property_button.gd │ │ └── property_button.gd.uid │ ├── property_type_picker │ │ ├── property_type_picker.gd │ │ ├── property_type_picker.gd.uid │ │ └── property_type_picker.tscn │ ├── resource_picker │ │ ├── resource_picker.gd │ │ ├── resource_picker.gd.uid │ │ └── resource_picker.tscn │ ├── script_picker │ │ ├── script_picker.gd │ │ ├── script_picker.gd.uid │ │ └── script_picker.tscn │ ├── texture_picker │ │ ├── texture_picker.gd │ │ ├── texture_picker.gd.uid │ │ └── texture_picker.tscn │ ├── update_button │ │ ├── update_button.gd │ │ ├── update_button.gd.uid │ │ └── update_button.tscn │ └── updater │ │ ├── updater.gd │ │ ├── updater.gd.uid │ │ └── updater.tscn └── editor │ ├── import_dialog │ ├── import_dialog.gd │ ├── import_dialog.gd.uid │ └── import_dialog.tscn │ ├── inspector │ ├── entity_category_browser_property.gd │ ├── entity_category_browser_property.gd.uid │ ├── entity_instance_browser_property.gd │ ├── entity_instance_browser_property.gd.uid │ ├── entity_instance_inspector.gd │ └── entity_instance_inspector.gd.uid │ ├── pandora_editor.gd │ ├── pandora_editor.gd.uid │ ├── pandora_editor.tscn │ ├── property_editor │ ├── property_editor.gd │ ├── property_editor.gd.uid │ └── property_editor.tscn │ └── property_settings_editor │ ├── property_settings_editor.gd │ ├── property_settings_editor.gd.uid │ └── property_settings_editor.tscn └── util ├── category_id_file_generator.gd ├── category_id_file_generator.gd.uid ├── compression.gd ├── compression.gd.uid ├── entity_id_file_generator.gd ├── entity_id_file_generator.gd.uid ├── id_generator.gd ├── id_generator.gd.uid ├── nanoid_generator.gd ├── nanoid_generator.gd.uid ├── script_util.gd ├── script_util.gd.uid ├── sequential_id_generator.gd ├── sequential_id_generator.gd.uid ├── tokenizer.gd └── tokenizer.gd.uid /addons/pandora/api.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bqv713p4thrli 2 | -------------------------------------------------------------------------------- /addons/pandora/backend/entity_backend.gd.uid: -------------------------------------------------------------------------------- 1 | uid://hd7urlwrtgyk 2 | -------------------------------------------------------------------------------- /addons/pandora/context/context_manager.gd: -------------------------------------------------------------------------------- 1 | ## Stores the pandora context. The context influences 2 | ## where user data will be stored. Change the context id 3 | ## to ensure custom save games for example. 4 | @tool 5 | class_name PandoraContextManager extends RefCounted 6 | 7 | # Notifies if the context has changed. 8 | # This can happen if someone changes save games. 9 | signal context_changed 10 | 11 | var _context_id: String = "" 12 | 13 | 14 | func set_context_id(new_context_id: String) -> void: 15 | _context_id = new_context_id 16 | 17 | 18 | func get_context_id() -> String: 19 | return _context_id 20 | -------------------------------------------------------------------------------- /addons/pandora/context/context_manager.gd.uid: -------------------------------------------------------------------------------- 1 | uid://wy0hx3e0e8rf 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Array.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Array.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://66qnm42libnj" 6 | path="res://.godot/imported/Array.svg-a46ab8298ee257f5a16f285b9bfb722e.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Array.svg" 15 | dest_files=["res://.godot/imported/Array.svg-a46ab8298ee257f5a16f285b9bfb722e.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/AssetLib.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/AssetLib.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dp8it1xjb8k2g" 6 | path="res://.godot/imported/AssetLib.svg-da7edefaec4f5631f9f4bbb2bb7ae4d1.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/AssetLib.svg" 15 | dest_files=["res://.godot/imported/AssetLib.svg-da7edefaec4f5631f9f4bbb2bb7ae4d1.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/AtlasTexture.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/AtlasTexture.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://rwodit05tms7" 6 | path="res://.godot/imported/AtlasTexture.svg-5056fe4707ffa264301261f449c31722.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/AtlasTexture.svg" 15 | dest_files=["res://.godot/imported/AtlasTexture.svg-5056fe4707ffa264301261f449c31722.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Clear.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Clear.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c8r1klbs37r1b" 6 | path="res://.godot/imported/Clear.svg-0b2da895d5f89295ced9a0d8c6f7878a.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Clear.svg" 15 | dest_files=["res://.godot/imported/Clear.svg-0b2da895d5f89295ced9a0d8c6f7878a.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Color.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Color.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c2738ylh13lsi" 6 | path="res://.godot/imported/Color.svg-79c67f8866a25395b767b76e5b6ff9b9.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Color.svg" 15 | dest_files=["res://.godot/imported/Color.svg-79c67f8866a25395b767b76e5b6ff9b9.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Edit.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Edit.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b0mt8ysdc5m4a" 6 | path="res://.godot/imported/Edit.svg-b452cfd6b4320d4a03672cd65cb72329.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Edit.svg" 15 | dest_files=["res://.godot/imported/Edit.svg-b452cfd6b4320d4a03672cd65cb72329.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Folder.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Folder.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dimpswbv6s8t2" 6 | path="res://.godot/imported/Folder.svg-1bac157773346222a10a5988ba2390e6.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Folder.svg" 15 | dest_files=["res://.godot/imported/Folder.svg-1bac157773346222a10a5988ba2390e6.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Font.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cjib8mo772gfh" 6 | path="res://.godot/imported/Font.svg-af5511d99ea1f05b3118bee68790c21f.ctex" 7 | metadata={ 8 | "editor_dark_theme": true, 9 | "editor_scale": 2.0, 10 | "has_editor_variant": true, 11 | "vram_texture": false 12 | } 13 | 14 | [deps] 15 | 16 | source_file="res://addons/pandora/icons/Font.svg" 17 | dest_files=["res://.godot/imported/Font.svg-af5511d99ea1f05b3118bee68790c21f.ctex"] 18 | 19 | [params] 20 | 21 | compress/mode=0 22 | compress/high_quality=false 23 | compress/lossy_quality=0.7 24 | compress/hdr_compression=1 25 | compress/normal_map=0 26 | compress/channel_pack=0 27 | mipmaps/generate=false 28 | mipmaps/limit=-1 29 | roughness/mode=0 30 | roughness/src_normal="" 31 | process/fix_alpha_border=true 32 | process/premult_alpha=false 33 | process/normal_map_invert_y=false 34 | process/hdr_as_srgb=false 35 | process/hdr_clamp_exposure=false 36 | process/size_limit=0 37 | detect_3d/compress_to=1 38 | svg/scale=1.0 39 | editor/scale_with_editor_scale=true 40 | editor/convert_colors_with_editor_theme=true 41 | -------------------------------------------------------------------------------- /addons/pandora/icons/KeyValue.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/KeyValue.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cb6str3hxrsdi" 6 | path="res://.godot/imported/KeyValue.svg-2822461d639d1a30d7677dfb5a40c9b5.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/KeyValue.svg" 15 | dest_files=["res://.godot/imported/KeyValue.svg-2822461d639d1a30d7677dfb5a40c9b5.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Object.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Object.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dojpd3ptnta4m" 6 | path="res://.godot/imported/Object.svg-cc61fbab643ed4cbb8bfe8ae40e25621.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Object.svg" 15 | dest_files=["res://.godot/imported/Object.svg-cc61fbab643ed4cbb8bfe8ae40e25621.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Reload.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Reload.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bhcep67ihojnd" 6 | path="res://.godot/imported/Reload.svg-8d99b6e330288432ea0139869560d96f.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Reload.svg" 15 | dest_files=["res://.godot/imported/Reload.svg-8d99b6e330288432ea0139869560d96f.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /addons/pandora/icons/Remove.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b2s1ixfakdj1e" 6 | path="res://.godot/imported/Remove.svg-304a522644e37fda7b1d0215ad410adc.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/pandora/icons/Remove.svg" 14 | dest_files=["res://.godot/imported/Remove.svg-304a522644e37fda7b1d0215ad410adc.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=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 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=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/pandora/icons/Save.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Save.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b8sd2702bmm7h" 6 | path="res://.godot/imported/Save.svg-14d8c562c46cec9ff174ae539c06d191.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Save.svg" 15 | dest_files=["res://.godot/imported/Save.svg-14d8c562c46cec9ff174ae539c06d191.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Search.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Search.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bv43ytfd0mlq1" 6 | path="res://.godot/imported/Search.svg-a90a8090ed63d36963e355a62f2bc45a.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Search.svg" 15 | dest_files=["res://.godot/imported/Search.svg-a90a8090ed63d36963e355a62f2bc45a.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/String.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/String.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://do5tkodyvid10" 6 | path="res://.godot/imported/String.svg-3b9e9f01cea93110daa936c3cf4f9623.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/String.svg" 15 | dest_files=["res://.godot/imported/String.svg-3b9e9f01cea93110daa936c3cf4f9623.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector2.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector2.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dcqltisjej0lu" 6 | path="res://.godot/imported/Vector2.svg-f2f9dce954f37bf7fb37a4d1ec1f0f9a.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Vector2.svg" 15 | dest_files=["res://.godot/imported/Vector2.svg-f2f9dce954f37bf7fb37a4d1ec1f0f9a.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector2i.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector2i.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bk7a4jkyif178" 6 | path="res://.godot/imported/Vector2i.svg-eeccc3f61eae845ad95d8e0ca54c343f.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Vector2i.svg" 15 | dest_files=["res://.godot/imported/Vector2i.svg-eeccc3f61eae845ad95d8e0ca54c343f.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector3.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector3.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bdxvds1pxhqv6" 6 | path="res://.godot/imported/Vector3.svg-28214985aa7422f5a8cd639de0a26885.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Vector3.svg" 15 | dest_files=["res://.godot/imported/Vector3.svg-28214985aa7422f5a8cd639de0a26885.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector3i.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/Vector3i.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c6kohe0abjrrs" 6 | path="res://.godot/imported/Vector3i.svg-fd64811289ff8c1fe119369f99b57b5c.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/Vector3i.svg" 15 | dest_files=["res://.godot/imported/Vector3i.svg-fd64811289ff8c1fe119369f99b57b5c.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/bool.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/bool.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://mmkaghs6sbx4" 6 | path="res://.godot/imported/bool.svg-43fb3fae2bb7e39a7a051a979b8ac2d3.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/bool.svg" 15 | dest_files=["res://.godot/imported/bool.svg-43fb3fae2bb7e39a7a051a979b8ac2d3.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/float.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/float.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cgupnims1vk7r" 6 | path="res://.godot/imported/float.svg-e237fdb12d4b5cee467fb0900459d2a1.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/float.svg" 15 | dest_files=["res://.godot/imported/float.svg-e237fdb12d4b5cee467fb0900459d2a1.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/pandora/48ec6e4c38cb92ef9d8b2be9a1fb77302ceadab4/addons/pandora/icons/icon.png -------------------------------------------------------------------------------- /addons/pandora/icons/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://crgjwaubao8pj" 6 | path="res://.godot/imported/icon.png-c66b8d90e029ace486d4c4b5320f5855.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/pandora/icons/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-c66b8d90e029ace486d4c4b5320f5855.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=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 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=1 35 | -------------------------------------------------------------------------------- /addons/pandora/icons/int.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/pandora/icons/int.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://blvy22tu53qxy" 6 | path="res://.godot/imported/int.svg-afe9a260ccab507b1ae9c13850b1afc8.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/int.svg" 15 | dest_files=["res://.godot/imported/int.svg-afe9a260ccab507b1ae9c13850b1afc8.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/nanoid.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 33 | 35 | 37 | 40 | 41 | 42 | 46 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /addons/pandora/icons/nanoid.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bfs83ic84umkv" 6 | path="res://.godot/imported/nanoid.svg-3c980c901e5edd8de536968665f465a0.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/nanoid.svg" 15 | dest_files=["res://.godot/imported/nanoid.svg-3c980c901e5edd8de536968665f465a0.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/pandora-icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dmh7pik8vjog8" 6 | path="res://.godot/imported/pandora-icon.svg-32440ea7d526187438f368f7b3906fad.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/pandora/icons/pandora-icon.svg" 15 | dest_files=["res://.godot/imported/pandora-icon.svg-32440ea7d526187438f368f7b3906fad.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=true 39 | -------------------------------------------------------------------------------- /addons/pandora/icons/pandora-json-icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://0aj3pk4ubvl7" 6 | path="res://.godot/imported/pandora-json-icon.svg-cbce8b7cbd8bcce6f1b57ba39adfd148.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/pandora/icons/pandora-json-icon.svg" 14 | dest_files=["res://.godot/imported/pandora-json-icon.svg-cbce8b7cbd8bcce6f1b57ba39adfd148.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=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 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=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/pandora/model/category.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PandoraCategory extends PandoraEntity 3 | 4 | # not persisted but computed at runtime 5 | var _children: Array[PandoraEntity] = [] 6 | 7 | 8 | func get_child(entity_id: String) -> PandoraEntity: 9 | for child in _children: 10 | if child.get_entity_id() == entity_id: 11 | return child 12 | return null 13 | 14 | 15 | func get_icon_path() -> String: 16 | if _icon_path != "": 17 | return _icon_path 18 | if _category_id != "" and get_category() != null and get_category()._icon_path != "": 19 | return get_category().get_icon_path() 20 | return "res://addons/pandora/icons/Folder.svg" 21 | 22 | 23 | func _delete_property(name: String) -> void: 24 | super._delete_property(name) 25 | for child in _children: 26 | child._delete_property(name) 27 | 28 | 29 | func _to_string() -> String: 30 | return "" 31 | -------------------------------------------------------------------------------- /addons/pandora/model/category.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ddi7nbxu0sys3 2 | -------------------------------------------------------------------------------- /addons/pandora/model/entity.gd.uid: -------------------------------------------------------------------------------- 1 | uid://8q73873xik7s 2 | -------------------------------------------------------------------------------- /addons/pandora/model/property.gd: -------------------------------------------------------------------------------- 1 | ## A property contains a Variant that can be accessed 2 | ## across Pandora by id. Properties can be shared between 3 | ## different entities (depending on the way they get inherited). 4 | ## This ensures that changing the original property will automatically 5 | ## apply to any other entity that is using it. 6 | class_name PandoraProperty extends RefCounted 7 | 8 | ## Emitted when the name of this property changed. 9 | signal name_changed(old_name: String, new_name: String) 10 | signal setting_changed(key: String) 11 | signal setting_cleared(key: String) 12 | 13 | var _id: String 14 | var _name: String: 15 | set(n): 16 | var old = _name 17 | _name = n 18 | if _name != old: 19 | name_changed.emit(old, _name) 20 | var _type: PandoraPropertyType 21 | var _default_value: Variant 22 | var _category_id: String 23 | # setting name -> Variant 24 | var _setting_overrides: Dictionary = {} 25 | 26 | 27 | func _init(id: String, name: String, type_name: String) -> void: 28 | self._id = id 29 | self._name = name 30 | self._type = PandoraPropertyType.lookup(type_name) 31 | self._default_value = _type.get_default_value() 32 | 33 | 34 | func get_setting(key: String) -> Variant: 35 | if has_setting_override(key): 36 | return _setting_overrides[key] 37 | elif _type != null: 38 | return _type.get_settings()[key]["value"] 39 | else: 40 | return null 41 | 42 | 43 | func get_setting_override(name: String) -> Variant: 44 | if _setting_overrides.has(name): 45 | return _setting_overrides[name] 46 | return null 47 | 48 | 49 | func has_setting_override(name: String) -> bool: 50 | return _setting_overrides.has(name) 51 | 52 | 53 | func set_setting_override(name: String, override: Variant) -> void: 54 | _setting_overrides[name] = override 55 | setting_changed.emit(name) 56 | 57 | 58 | func clear_setting_override(name: String) -> void: 59 | _setting_overrides.erase(name) 60 | setting_cleared.emit(name) 61 | 62 | 63 | func set_default_value(value: Variant) -> void: 64 | if not _type.is_valid(value): 65 | print("Pandora error: value " + str(value) + " is incompatible with type ", _type) 66 | return 67 | # ensure that a supported type is assigned. 68 | if value is PandoraEntity: 69 | value = PandoraReference.new( 70 | value.get_entity_id(), 71 | ( 72 | PandoraReference.Type.CATEGORY 73 | if value is PandoraCategory 74 | else PandoraReference.Type.ENTITY 75 | ) 76 | ) 77 | _default_value = value 78 | 79 | 80 | func get_property_id() -> String: 81 | return _id 82 | 83 | 84 | func get_property_name() -> String: 85 | return _name 86 | 87 | 88 | func get_property_type() -> PandoraPropertyType: 89 | return _type 90 | 91 | 92 | func get_default_value() -> Variant: 93 | if _default_value is PandoraReference: 94 | return _default_value.get_entity() 95 | return _default_value 96 | 97 | 98 | func get_category_id() -> String: 99 | return _category_id 100 | 101 | 102 | ## the original category id specifies 103 | ## the category where this property has 104 | ## been originally defined (and inherited down) 105 | func get_original_category_id() -> String: 106 | return _category_id 107 | 108 | 109 | ## resets this property to its original 110 | ## default value in case it was overridden 111 | func reset_to_default() -> void: 112 | pass 113 | 114 | 115 | ## true in case this property is the original definition 116 | ## of a property. (not inherited) 117 | func is_original() -> bool: 118 | return true 119 | 120 | 121 | ## returns true when this property is currently overridden 122 | func is_overridden() -> bool: 123 | return false 124 | 125 | 126 | func load_data(data: Dictionary) -> void: 127 | _id = data["_id"] 128 | _name = data["_name"] 129 | _type = PandoraPropertyType.lookup(data["_type"]) 130 | _category_id = data["_category_id"] 131 | if data.has("_setting_overrides"): 132 | _setting_overrides = data["_setting_overrides"] 133 | _default_value = _type.parse_value(data["_default_value"], data["_setting_overrides"]) 134 | else: 135 | _default_value = _type.parse_value(data["_default_value"]) 136 | 137 | 138 | func save_data() -> Dictionary: 139 | var data = { 140 | "_id": _id, 141 | "_name": _name, 142 | "_type": _type.get_type_name(), 143 | "_default_value": _type.write_value(_default_value), 144 | "_category_id": _category_id, 145 | } 146 | if not _setting_overrides.is_empty(): 147 | data["_setting_overrides"] = _setting_overrides 148 | return data 149 | 150 | 151 | func equals(other: PandoraProperty) -> bool: 152 | return ( 153 | get_property_id() == other.get_property_id() 154 | and get_property_name() == other.get_property_name() 155 | and get_property_type() == other.get_property_type() 156 | ) 157 | 158 | 159 | func _to_string() -> String: 160 | return ( 161 | "" 166 | ) 167 | -------------------------------------------------------------------------------- /addons/pandora/model/property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cu448of86n1i3 2 | -------------------------------------------------------------------------------- /addons/pandora/model/property_instance.gd: -------------------------------------------------------------------------------- 1 | class_name PandoraPropertyInstance extends Resource 2 | 3 | var _property_id: String 4 | var _value: Variant 5 | # not persisted -> set at runtime 6 | var _property: PandoraProperty 7 | 8 | 9 | func _init(_property: PandoraProperty, value: Variant) -> void: 10 | if _property != null: 11 | self._property_id = _property.get_property_id() 12 | self._value = value 13 | self._property = _property 14 | 15 | 16 | func duplicate_instance() -> PandoraPropertyInstance: 17 | return PandoraPropertyInstance.new(_property, _value) 18 | 19 | 20 | func get_property_name() -> String: 21 | if _property != null: 22 | return _property.get_property_name() 23 | return "" 24 | 25 | 26 | func get_property_id() -> String: 27 | return _property_id 28 | 29 | 30 | func get_property_value() -> Variant: 31 | return _value 32 | 33 | 34 | func set_property_value(value: Variant) -> void: 35 | self._value = value 36 | 37 | 38 | func load_data(data: Dictionary) -> void: 39 | _property_id = data["_property_id"] 40 | _property = Pandora.get_property(_property_id) as PandoraProperty 41 | var type = _property.get_property_type() as PandoraPropertyType 42 | _value = type.parse_value(data["_value"]) 43 | 44 | 45 | func save_data() -> Dictionary: 46 | var type = _property.get_property_type() as PandoraPropertyType 47 | return {"_value": type.write_value(_value), "_property_id": _property_id} 48 | 49 | 50 | func _to_string() -> String: 51 | return "" 52 | -------------------------------------------------------------------------------- /addons/pandora/model/property_instance.gd.uid: -------------------------------------------------------------------------------- 1 | uid://kxm5rqslpngs 2 | -------------------------------------------------------------------------------- /addons/pandora/model/reference.gd: -------------------------------------------------------------------------------- 1 | class_name PandoraReference extends RefCounted 2 | 3 | enum Type { ENTITY, CATEGORY } 4 | 5 | var _entity_id: String 6 | var _type: Type 7 | 8 | 9 | func _init(entity_id: String, type: Type) -> void: 10 | _entity_id = entity_id 11 | _type = type 12 | 13 | 14 | func get_entity() -> PandoraEntity: 15 | if _type == Type.ENTITY: 16 | return Pandora.get_entity(_entity_id) 17 | else: 18 | return Pandora.get_category(_entity_id) 19 | 20 | 21 | func load_data(data: Dictionary) -> void: 22 | _entity_id = data["_entity_id"] 23 | if data.has("_type"): 24 | _type = data["_type"] 25 | else: 26 | _type = Type.ENTITY 27 | 28 | 29 | func save_data() -> Dictionary: 30 | return {"_entity_id": _entity_id, "_type": _type} 31 | 32 | 33 | func _to_string() -> String: 34 | return "" 35 | -------------------------------------------------------------------------------- /addons/pandora/model/reference.gd.uid: -------------------------------------------------------------------------------- 1 | uid://kq36ufctf202 2 | -------------------------------------------------------------------------------- /addons/pandora/model/type.gd: -------------------------------------------------------------------------------- 1 | ## This class allows us to make it easier 2 | ## to specify new types, e.g. by adding a new file 3 | ## which then internally specifies everything Pandora needs to know, 4 | ## including serialization, property settings and validation. 5 | class_name PandoraPropertyType extends RefCounted 6 | 7 | 8 | class UndefinedType: 9 | extends PandoraPropertyType 10 | 11 | func _init(): 12 | super("undefined", {}, null, "") 13 | 14 | 15 | var _type_name: String 16 | var _settings: Dictionary 17 | var _default_value: Variant 18 | var _type_icon_path: String 19 | 20 | 21 | func _init( 22 | type_name: String, settings: Dictionary, default_value: Variant, type_icon_path: String 23 | ) -> void: 24 | self._type_name = type_name 25 | self._settings = settings 26 | self._default_value = default_value 27 | self._type_icon_path = type_icon_path 28 | 29 | 30 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 31 | return variant 32 | 33 | 34 | func write_value(variant: Variant) -> Variant: 35 | return variant 36 | 37 | 38 | func get_type_name() -> String: 39 | return _type_name 40 | 41 | 42 | func get_settings() -> Dictionary: 43 | return _settings 44 | 45 | 46 | func has_settings() -> bool: 47 | return _settings.is_empty() == false 48 | 49 | 50 | func get_default_value() -> Variant: 51 | return _default_value 52 | 53 | 54 | func get_type_icon_path() -> String: 55 | return _type_icon_path 56 | 57 | 58 | func is_valid(variant: Variant) -> bool: 59 | return false 60 | 61 | 62 | func allow_nesting() -> bool: 63 | return true 64 | 65 | 66 | static func lookup(name: String) -> PandoraPropertyType: 67 | if name == "": 68 | return UndefinedType.new() 69 | 70 | var klass = PandoraPropertyType 71 | var types_dir = klass.resource_path.get_base_dir() 72 | var type_path = types_dir + "/types/" + name + ".gd" 73 | 74 | if ResourceLoader.exists(type_path): 75 | var ScriptType = load(type_path) 76 | if ScriptType != null: 77 | if ScriptType.has_source_code() or ScriptType.can_instantiate(): 78 | return ScriptType.new() 79 | 80 | return UndefinedType.new() 81 | 82 | 83 | static func get_all_types() -> Array[PandoraPropertyType]: 84 | var types: Array[PandoraPropertyType] = [] 85 | var dir = DirAccess.open("res://addons/pandora/model/types") 86 | dir.list_dir_begin() 87 | var file_name: String = dir.get_next() 88 | while file_name != "": 89 | if file_name.ends_with(".gd"): 90 | var type_name = file_name.left(file_name.length() - 3) 91 | var type = lookup(type_name) 92 | if type != null: 93 | types.append(type) 94 | file_name = dir.get_next() 95 | dir.list_dir_end() 96 | return types 97 | -------------------------------------------------------------------------------- /addons/pandora/model/type.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dn82xgmfu5q7t 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/array.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Array.svg" 4 | 5 | const SETTING_ARRAY_TYPE = "Array Type" 6 | 7 | const SETTINGS = { 8 | SETTING_ARRAY_TYPE: 9 | { 10 | "type": "property_type", 11 | "value": "string", 12 | } 13 | } 14 | 15 | 16 | func _init() -> void: 17 | super("array", SETTINGS, [], ICON_PATH) 18 | 19 | 20 | func is_valid(variant: Variant) -> bool: 21 | return variant is Array 22 | 23 | 24 | func get_merged_settings(property: PandoraProperty) -> Dictionary: 25 | var merged_settings: Dictionary = _settings.duplicate() 26 | var array_type = PandoraPropertyType.lookup(property.get_setting(SETTING_ARRAY_TYPE)) 27 | var array_type_settings: Dictionary = array_type.get_settings().duplicate() 28 | merged_settings.merge(array_type_settings) 29 | return merged_settings 30 | 31 | 32 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 33 | if variant is Dictionary: 34 | var array = [] 35 | var dict = variant as Dictionary 36 | for i in range(dict.size()): 37 | var value = dict[str(i)] 38 | if not settings.is_empty(): 39 | if settings[SETTING_ARRAY_TYPE] == "reference" and "_entity_id" in value: 40 | value = PandoraReference.new(value["_entity_id"], value["_type"]).get_entity() 41 | if settings[SETTING_ARRAY_TYPE] == "resource": 42 | value = load(value) 43 | if settings[SETTING_ARRAY_TYPE] == "color": 44 | value = Color.from_string(value, Color.WHITE) 45 | else: 46 | if value is Dictionary and value.has("type") and value.has("value"): 47 | var value_type = value["type"] 48 | var dict_value = value["value"] 49 | 50 | var type = PandoraPropertyType.lookup(value_type) 51 | if type != null: 52 | value = type.parse_value(dict_value) 53 | 54 | array.append(value) 55 | return array 56 | return variant 57 | 58 | 59 | func write_value(variant: Variant) -> Variant: 60 | var array = variant as Array 61 | var dict = {} 62 | if not array.is_empty(): 63 | var types = PandoraPropertyType.get_all_types() 64 | var value_type 65 | 66 | for i in range(array.size()): 67 | var value = array[i] 68 | if value is PandoraEntity: 69 | value_type = PandoraPropertyType.lookup("reference") 70 | value = ( 71 | PandoraReference 72 | . new( 73 | value.get_entity_id(), 74 | ( 75 | PandoraReference.Type.CATEGORY 76 | if value is PandoraCategory 77 | else PandoraReference.Type.ENTITY 78 | ) 79 | ) 80 | . save_data() 81 | ) 82 | elif value is PandoraReference: 83 | value_type = PandoraPropertyType.lookup("reference") 84 | value = value.save_data() 85 | else: 86 | for type in types: 87 | if type.is_valid(value): 88 | value_type = type 89 | value = type.write_value(value) 90 | break 91 | 92 | if value != null: 93 | if value_type == null: 94 | dict[str(i)] = value 95 | else: 96 | dict[str(i)] = {"type": value_type.get_type_name(), "value": value} 97 | 98 | return dict 99 | 100 | 101 | func allow_nesting() -> bool: 102 | return false 103 | -------------------------------------------------------------------------------- /addons/pandora/model/types/array.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b0b632vedutjw 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/bool.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/bool.svg" 4 | 5 | const SETTINGS = {} 6 | 7 | 8 | func _init() -> void: 9 | super("bool", SETTINGS, false, ICON_PATH) 10 | 11 | 12 | func is_valid(variant: Variant) -> bool: 13 | return variant is bool 14 | -------------------------------------------------------------------------------- /addons/pandora/model/types/bool.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cbc5oo6r7ghbj 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/color.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Color.svg" 4 | 5 | const SETTINGS = {} 6 | 7 | 8 | func _init() -> void: 9 | super("color", SETTINGS, Color.WHITE, ICON_PATH) 10 | 11 | 12 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 13 | if variant is String: 14 | return Color.from_string(variant, Color.WHITE) 15 | return variant 16 | 17 | 18 | func write_value(variant: Variant) -> Variant: 19 | var color = variant as Color 20 | return color.to_html() 21 | 22 | 23 | func is_valid(variant: Variant) -> bool: 24 | return variant is Color or variant is String 25 | -------------------------------------------------------------------------------- /addons/pandora/model/types/color.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d2xr4whsmpnnw 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/float.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/float.svg" 4 | 5 | const SETTING_MIN_VALUE = "Min Value" 6 | const SETTING_MAX_VALUE = "Max Value" 7 | const SETTING_STEPS = "Steps" 8 | 9 | const SETTINGS = { 10 | SETTING_STEPS: {"type": "float", "value": 0.01}, 11 | SETTING_MIN_VALUE: {"type": "int", "value": -9999999999}, 12 | SETTING_MAX_VALUE: {"type": "int", "value": 9999999999} 13 | } 14 | 15 | 16 | func _init() -> void: 17 | super("float", SETTINGS, 0.0, ICON_PATH) 18 | 19 | 20 | func is_valid(variant: Variant) -> bool: 21 | return variant is float 22 | -------------------------------------------------------------------------------- /addons/pandora/model/types/float.gd.uid: -------------------------------------------------------------------------------- 1 | uid://by7c2yf0lqsg7 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/int.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/int.svg" 4 | 5 | const SETTING_MIN_VALUE = "Min Value" 6 | const SETTING_MAX_VALUE = "Max Value" 7 | 8 | const SETTINGS = { 9 | SETTING_MIN_VALUE: {"type": "int", "value": -9999999999}, 10 | SETTING_MAX_VALUE: {"type": "int", "value": 9999999999} 11 | } 12 | 13 | 14 | func _init() -> void: 15 | super("int", SETTINGS, 0, ICON_PATH) 16 | 17 | 18 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 19 | if variant is float: 20 | return int(variant) 21 | return variant 22 | 23 | 24 | func is_valid(variant: Variant) -> bool: 25 | return variant is int 26 | -------------------------------------------------------------------------------- /addons/pandora/model/types/int.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cn8krkhtccirc 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/reference.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Object.svg" 4 | 5 | const SETTING_CATEGORIES_ONLY = "Categories Only" 6 | const SETTING_CATEGORY_FILTER = "Category Filter" 7 | const SETTING_SORT_LIST = "Sort List" 8 | const SORT_ALPHABETICALLY = "Alphabetically" 9 | const SORT_AS_IS = "As-Is" 10 | 11 | const SETTINGS = { 12 | SETTING_CATEGORIES_ONLY: {"type": "bool", "value": false}, 13 | SETTING_CATEGORY_FILTER: {"type": "reference", "value": ""}, 14 | SETTING_SORT_LIST: 15 | {"type": "string", "options": [SORT_ALPHABETICALLY, SORT_AS_IS], "value": SORT_ALPHABETICALLY} 16 | } 17 | 18 | 19 | func _init() -> void: 20 | super("reference", SETTINGS, null, ICON_PATH) 21 | 22 | 23 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 24 | if variant is Dictionary: 25 | var reference = PandoraReference.new("", 0) 26 | reference.load_data(variant) 27 | return reference 28 | return variant 29 | 30 | 31 | func write_value(variant: Variant) -> Variant: 32 | if variant is PandoraReference: 33 | return variant.save_data() 34 | return variant 35 | 36 | 37 | func is_valid(variant: Variant) -> bool: 38 | return variant is PandoraEntity 39 | -------------------------------------------------------------------------------- /addons/pandora/model/types/reference.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bs8pju4quv8m3 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/resource.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/AtlasTexture.svg" 4 | 5 | const SETTINGS = {} 6 | 7 | 8 | func _init() -> void: 9 | super("resource", SETTINGS, null, ICON_PATH) 10 | 11 | 12 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 13 | if variant is String: 14 | return load(variant) 15 | return variant 16 | 17 | 18 | func write_value(variant: Variant) -> Variant: 19 | if variant is Resource: 20 | return variant.resource_path 21 | return variant 22 | 23 | 24 | func is_valid(variant: Variant) -> bool: 25 | return variant is Resource 26 | -------------------------------------------------------------------------------- /addons/pandora/model/types/resource.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cy0nw76c56kl7 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/string.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/String.svg" 4 | 5 | const SETTINGS = {} 6 | 7 | 8 | func _init() -> void: 9 | super("string", SETTINGS, "", ICON_PATH) 10 | 11 | 12 | func is_valid(variant: Variant) -> bool: 13 | return variant is String 14 | -------------------------------------------------------------------------------- /addons/pandora/model/types/string.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bmmbuipddr40v 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector2.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Vector2.svg" 4 | 5 | const SETTING_MIN_COMPONENT_VALUE = "Min Component Value" 6 | const SETTING_MAX_COMPONENT_VALUE = "Max Component Value" 7 | const SETTING_STEPS = "Steps" 8 | 9 | const SETTINGS = { 10 | SETTING_STEPS: {"type": "float", "value": 0.01}, 11 | SETTING_MIN_COMPONENT_VALUE: {"type": "int", "value": -9999999999}, 12 | SETTING_MAX_COMPONENT_VALUE: {"type": "int", "value": 9999999999} 13 | } 14 | 15 | 16 | func _init() -> void: 17 | super("vector2", SETTINGS, Vector2.ZERO, ICON_PATH) 18 | 19 | 20 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 21 | if variant is String: 22 | var values = variant.trim_prefix("(").trim_suffix(")").split(",", false, 1) 23 | return Vector2(values[0].to_float(), values[1].to_float()) 24 | return variant 25 | 26 | 27 | func write_value(variant: Variant) -> Variant: 28 | var vector = variant as Vector2 29 | return "(%s,%s)" % [vector.x, vector.y] 30 | 31 | 32 | func is_valid(variant: Variant) -> bool: 33 | return variant is Vector2 34 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector2.gd.uid: -------------------------------------------------------------------------------- 1 | uid://rbxug87suaxl 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector2i.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Vector2i.svg" 4 | 5 | const SETTING_MIN_COMPONENT_VALUE = "Min Component Value" 6 | const SETTING_MAX_COMPONENT_VALUE = "Max Component Value" 7 | 8 | const SETTINGS = { 9 | SETTING_MIN_COMPONENT_VALUE: {"type": "int", "value": -9999999999}, 10 | SETTING_MAX_COMPONENT_VALUE: {"type": "int", "value": 9999999999} 11 | } 12 | 13 | 14 | func _init() -> void: 15 | super("vector2i", SETTINGS, Vector2i.ZERO, ICON_PATH) 16 | 17 | 18 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 19 | if variant is String: 20 | var values = variant.trim_prefix("(").trim_suffix(")").split(",", false, 1) 21 | return Vector2i(values[0].to_int(), values[1].to_int()) 22 | return variant 23 | 24 | 25 | func write_value(variant: Variant) -> Variant: 26 | var vector = variant as Vector2i 27 | return "(%s,%s)" % [vector.x, vector.y] 28 | 29 | 30 | func is_valid(variant: Variant) -> bool: 31 | return variant is Vector2i 32 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector2i.gd.uid: -------------------------------------------------------------------------------- 1 | uid://0fhypi3gtmud 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector3.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Vector3.svg" 4 | 5 | const SETTING_MIN_COMPONENT_VALUE = "Min Component Value" 6 | const SETTING_MAX_COMPONENT_VALUE = "Max Component Value" 7 | const SETTING_STEPS = "Steps" 8 | 9 | const SETTINGS = { 10 | SETTING_STEPS: {"type": "float", "value": 0.01}, 11 | SETTING_MIN_COMPONENT_VALUE: {"type": "int", "value": -9999999999}, 12 | SETTING_MAX_COMPONENT_VALUE: {"type": "int", "value": 9999999999} 13 | } 14 | 15 | 16 | func _init() -> void: 17 | super("vector3", SETTINGS, Vector3.ZERO, ICON_PATH) 18 | 19 | 20 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 21 | if variant is String: 22 | var values = variant.trim_prefix("(").trim_suffix(")").split(",", false, 2) 23 | return Vector3(values[0].to_float(), values[1].to_float(), values[2].to_float()) 24 | return variant 25 | 26 | 27 | func write_value(variant: Variant) -> Variant: 28 | var vector = variant as Vector3 29 | return "(%s,%s,%s)" % [vector.x, vector.y, vector.z] 30 | 31 | 32 | func is_valid(variant: Variant) -> bool: 33 | return variant is Vector3 34 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector3.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bfot6pj52pnnv 2 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector3i.gd: -------------------------------------------------------------------------------- 1 | extends PandoraPropertyType 2 | 3 | const ICON_PATH = "res://addons/pandora/icons/Vector3i.svg" 4 | 5 | const SETTING_MIN_COMPONENT_VALUE = "Min Component Value" 6 | const SETTING_MAX_COMPONENT_VALUE = "Max Component Value" 7 | 8 | const SETTINGS = { 9 | SETTING_MIN_COMPONENT_VALUE: {"type": "int", "value": -9999999999}, 10 | SETTING_MAX_COMPONENT_VALUE: {"type": "int", "value": 9999999999} 11 | } 12 | 13 | 14 | func _init() -> void: 15 | super("vector3i", SETTINGS, Vector3i.ZERO, ICON_PATH) 16 | 17 | 18 | func parse_value(variant: Variant, settings: Dictionary = {}) -> Variant: 19 | if variant is String: 20 | var values = variant.trim_prefix("(").trim_suffix(")").split(",", false, 2) 21 | return Vector3i(values[0].to_int(), values[1].to_int(), values[2].to_int()) 22 | return variant 23 | 24 | 25 | func write_value(variant: Variant) -> Variant: 26 | var vector = variant as Vector3i 27 | return "(%s,%s,%s)" % [vector.x, vector.y, vector.z] 28 | 29 | 30 | func is_valid(variant: Variant) -> bool: 31 | return variant is Vector3i 32 | -------------------------------------------------------------------------------- /addons/pandora/model/types/vector3i.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ckafq5g2cg7dq 2 | -------------------------------------------------------------------------------- /addons/pandora/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Pandora" 4 | description="📦 Store items, inventories, spells, mobs and more." 5 | author="bitbrain" 6 | version="1.0-alpha10-dev" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/pandora/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const PandoraEditor := preload("res://addons/pandora/ui/editor/pandora_editor.tscn") 5 | const PandoraIcon := preload("res://addons/pandora/icons/pandora-icon.svg") 6 | const PandoraEntityInspector = preload("res://addons/pandora/ui/editor/inspector/entity_instance_inspector.gd") 7 | const Compression = preload("res://addons/pandora/util/compression.gd") 8 | 9 | var editor_view 10 | var entity_inspector 11 | var _exporter: PandoraExportPlugin 12 | 13 | func _init() -> void: 14 | self.name = 'PandoraPlugin' 15 | 16 | 17 | func _enter_tree() -> void: 18 | Engine.set_meta("PandoraEditorPlugin", self) 19 | _exporter = PandoraExportPlugin.new() 20 | add_autoload_singleton("Pandora", "res://addons/pandora/api.gd") 21 | add_export_plugin(_exporter) 22 | PandoraSettings.initialize() 23 | 24 | if Engine.is_editor_hint(): 25 | editor_view = PandoraEditor.instantiate() 26 | editor_view.hide() 27 | get_editor_interface().get_editor_main_screen().add_child(editor_view) 28 | 29 | # connect signals for error handling 30 | get_editor_interface().get_resource_filesystem().resources_reimported.connect(func(res): if editor_view.has_method("reattempt_load_on_error"): editor_view.reattempt_load_on_error()) 31 | get_editor_interface().get_resource_filesystem().sources_changed.connect(func(res): if editor_view.has_method("reattempt_load_on_error"): editor_view.reattempt_load_on_error()) 32 | get_editor_interface().get_resource_filesystem().resources_reload.connect(func(exists): if editor_view.has_method("reattempt_load_on_error"): editor_view.reattempt_load_on_error()) 33 | get_editor_interface().get_resource_filesystem().script_classes_updated.connect(func(): if editor_view.has_method("reattempt_load_on_error"): editor_view.reattempt_load_on_error()) 34 | 35 | entity_inspector = PandoraEntityInspector.new() 36 | add_inspector_plugin(entity_inspector) 37 | 38 | _make_visible(false) 39 | 40 | 41 | func _apply_changes() -> void: 42 | if Engine.is_editor_hint() and is_instance_valid(editor_view): 43 | if editor_view.has_method("apply_changes"): 44 | editor_view.apply_changes() 45 | 46 | 47 | func _exit_tree() -> void: 48 | if Engine.is_editor_hint() and is_instance_valid(editor_view): 49 | remove_control_from_bottom_panel(editor_view) 50 | editor_view.queue_free() 51 | remove_inspector_plugin(entity_inspector) 52 | 53 | Engine.remove_meta("PandoraEditorPlugin") 54 | 55 | remove_export_plugin(_exporter) 56 | remove_autoload_singleton("Pandora") 57 | 58 | 59 | func _make_visible(visible:bool) -> void: 60 | if Engine.is_editor_hint() and is_instance_valid(editor_view): 61 | editor_view.visible = visible 62 | 63 | 64 | func _has_main_screen() -> bool: 65 | return Engine.is_editor_hint() 66 | 67 | 68 | func _get_plugin_name() -> String: 69 | return "Pandora" 70 | 71 | 72 | func _get_plugin_icon() -> Texture2D: 73 | return PandoraIcon 74 | 75 | class PandoraExportPlugin extends EditorExportPlugin: 76 | # Override the _export_begin method to add the data.pandora file during export 77 | func _export_begin(features: PackedStringArray, is_debug: bool, path: String, flags: int): 78 | var pandora_path = "res://data.pandora" 79 | var file = FileAccess.open(pandora_path, FileAccess.READ) 80 | if not file: 81 | printerr("Unable to export Pandora data: ", FileAccess.get_open_error()) 82 | return 83 | var data:PackedByteArray = file.get_buffer(file.get_length()) 84 | if not is_debug: 85 | var text = file.get_as_text() 86 | data = Compression.compress(text) 87 | add_file(pandora_path, data, false) 88 | 89 | func _get_name() -> String: 90 | return "PandoraExporter" 91 | -------------------------------------------------------------------------------- /addons/pandora/plugin.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b65tct4x0yii8 2 | -------------------------------------------------------------------------------- /addons/pandora/settings/pandora_settings.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PandoraSettings 3 | extends RefCounted 4 | 5 | enum IDType { 6 | SEQUENTIAL, 7 | NANOID, 8 | } 9 | 10 | const CATEGORY_MAIN: StringName = "pandora" 11 | const CATEGORY_CONFIG: StringName = CATEGORY_MAIN + "/config" 12 | 13 | const SETTING_ID_TYPE: StringName = CATEGORY_CONFIG + "/id_type" 14 | 15 | const DEFAULT_ID_TYPE: IDType = IDType.SEQUENTIAL 16 | 17 | 18 | static func initialize() -> void: 19 | init_setting( 20 | SETTING_ID_TYPE, 21 | IDType.keys()[DEFAULT_ID_TYPE], 22 | TYPE_STRING, 23 | PROPERTY_HINT_ENUM, 24 | "%s,%s" % IDType.keys() 25 | ) 26 | 27 | 28 | static func init_setting( 29 | name: String, 30 | default: Variant, 31 | type := typeof(default), 32 | hint := PROPERTY_HINT_NONE, 33 | hint_string := "" 34 | ) -> void: 35 | if not ProjectSettings.has_setting(name): 36 | ProjectSettings.set_setting(name, default) 37 | 38 | ProjectSettings.set_initial_value(name, default) 39 | 40 | var info = { 41 | "name": name, 42 | "type": type, 43 | "hint": hint, 44 | "hint_string": hint_string, 45 | } 46 | ProjectSettings.add_property_info(info) 47 | 48 | 49 | static func get_id_type() -> IDType: 50 | var default: StringName = IDType.keys()[DEFAULT_ID_TYPE] 51 | var key := ProjectSettings.get_setting(SETTING_ID_TYPE, default) 52 | return IDType[key] 53 | 54 | 55 | static func set_id_type(id_type: IDType) -> void: 56 | ProjectSettings.set_setting(SETTING_ID_TYPE, IDType.keys()[id_type]) 57 | -------------------------------------------------------------------------------- /addons/pandora/settings/pandora_settings.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d2higq0hv04lf 2 | -------------------------------------------------------------------------------- /addons/pandora/storage/data_storage.gd: -------------------------------------------------------------------------------- 1 | ## Generic definition of a Pandora storage. 2 | ## Provides a generic way to persist and load 3 | ## data required for this addon. 4 | class_name PandoraDataStorage extends RefCounted 5 | 6 | var DEFAULT_ICON = preload("res://addons/pandora/icons/pandora-icon.svg") 7 | 8 | 9 | func get_storage_name() -> String: 10 | return "missing storage name" 11 | 12 | 13 | func get_storage_description() -> String: 14 | return "missing description" 15 | 16 | 17 | func get_storage_icon() -> Texture: 18 | return DEFAULT_ICON 19 | 20 | 21 | func store_all_data(data: Dictionary, context_id: String) -> Dictionary: 22 | return {} 23 | 24 | 25 | func get_all_data(context_id: String) -> Dictionary: 26 | return {} 27 | 28 | 29 | func load_from_file(file_path: String) -> Dictionary: 30 | return {} 31 | -------------------------------------------------------------------------------- /addons/pandora/storage/data_storage.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cofymmkr5biwu 2 | -------------------------------------------------------------------------------- /addons/pandora/storage/json/json_data_storage.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PandoraJsonDataStorage extends PandoraDataStorage 3 | 4 | const ICON = preload("res://addons/pandora/icons/pandora-json-icon.svg") 5 | 6 | var data_directory: String = "user://pandora" 7 | 8 | 9 | func get_backend_name() -> String: 10 | return "Pandora JSON" 11 | 12 | 13 | func get_backend_description() -> String: 14 | return "Stores data via json at the data_directory provided." 15 | 16 | 17 | func get_backend_icon() -> Texture: 18 | return ICON 19 | 20 | 21 | func _init(data_dir: String): 22 | data_directory = data_dir 23 | 24 | 25 | func store_all_data(data: Dictionary, context_id: String) -> Dictionary: 26 | var file_path = _get_file_path(context_id) 27 | var file: FileAccess 28 | if _should_use_compression(): 29 | file = FileAccess.open_compressed(file_path, FileAccess.WRITE) 30 | file.store_string(JSON.stringify(data)) 31 | else: 32 | file = FileAccess.open(file_path, FileAccess.WRITE) 33 | file.store_string(JSON.stringify(data, "\t")) 34 | file.close() 35 | return data 36 | 37 | 38 | func get_all_data(context_id: String) -> Dictionary: 39 | var file_path = _get_file_path(context_id) 40 | var file: FileAccess 41 | if _should_use_compression(): 42 | file = FileAccess.open_compressed(file_path, FileAccess.READ) 43 | else: 44 | file = FileAccess.open(file_path, FileAccess.READ) 45 | var json: JSON = JSON.new() 46 | if file != null: 47 | var text = file.get_as_text() 48 | json.parse(text) 49 | file.close() 50 | # Backwards compatibility for already compressed files 51 | if json.get_data() == null and OS.is_debug_build(): 52 | print("Compressed file detected in debug mode, decompressing...") 53 | return get_decompressed_data(file_path) 54 | return json.get_data() as Dictionary 55 | else: 56 | return {} 57 | 58 | 59 | func get_decompressed_data(file_path: String) -> Dictionary: 60 | var file: FileAccess = FileAccess.open_compressed(file_path, FileAccess.READ) 61 | if file != null: 62 | var text = file.get_as_text() 63 | file.close() 64 | var json: JSON = JSON.new() 65 | json.parse(text) 66 | return json.get_data() as Dictionary 67 | else: 68 | return {} 69 | 70 | 71 | func load_from_file(file_path: String) -> Dictionary: 72 | var file: FileAccess 73 | if _should_use_compression(): 74 | file = FileAccess.open_compressed(file_path, FileAccess.READ) 75 | else: 76 | file = FileAccess.open(file_path, FileAccess.READ) 77 | if FileAccess.file_exists(file_path) and file != null: 78 | var content = file.get_as_text() 79 | file.close() 80 | var json = JSON.new() 81 | json.parse(content) 82 | return json.get_data() 83 | else: 84 | return {} 85 | 86 | 87 | func _get_directory_path(context_id: String) -> String: 88 | var directory_path = "" 89 | if data_directory.ends_with("//"): 90 | directory_path = ( 91 | "%s%s" % [data_directory, context_id] if context_id != "" else data_directory 92 | ) 93 | else: 94 | directory_path = ( 95 | "%s/%s" % [data_directory, context_id] if context_id != "" else data_directory 96 | ) 97 | if not DirAccess.dir_exists_absolute(directory_path): 98 | DirAccess.make_dir_recursive_absolute(directory_path) 99 | return "%s" % [directory_path] 100 | 101 | 102 | func _get_file_path(context_id: String) -> String: 103 | return "%s/data.pandora" % [_get_directory_path(context_id)] 104 | 105 | 106 | func _should_use_compression() -> bool: 107 | # Ensure within the Godot Engine editor Pandora remains uncompressed 108 | return not Engine.is_editor_hint() and not OS.is_debug_build() 109 | -------------------------------------------------------------------------------- /addons/pandora/storage/json/json_data_storage.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bk527fxef7usp 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_editor.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | 4 | signal item_added(item: Variant) 5 | signal item_removed(item: Variant) 6 | signal item_updated(idx: int, new_item: Variant) 7 | signal about_to_popup 8 | 9 | @onready var edit_button: Button = $EditArrayButton 10 | @onready var array_info: LineEdit = $ArrayInfo 11 | @onready var array_window: Window = $ArrayWindow 12 | 13 | var _property: PandoraProperty 14 | 15 | func _ready(): 16 | _refresh() 17 | edit_button.pressed.connect(func(): array_window.open(_property)) 18 | array_window.about_to_popup.connect(func(): about_to_popup.emit()) 19 | array_window.item_added.connect(func(item: Variant): 20 | item_added.emit(item) 21 | _refresh.call_deferred() 22 | ) 23 | array_window.item_removed.connect(func(item: Variant): 24 | item_removed.emit(item) 25 | _refresh.call_deferred() 26 | ) 27 | array_window.item_updated.connect(func(idx: int, item: Variant): 28 | item_updated.emit(idx, item) 29 | _refresh.call_deferred() 30 | ) 31 | 32 | func set_property(property: PandoraProperty): 33 | if property.get_original_category_id() != property._id: 34 | var original_category = Pandora.get_category(property.get_original_category_id()) 35 | var original_array_property = original_category.get_entity_property(property.get_property_name()) 36 | property._setting_overrides = original_array_property._setting_overrides 37 | _property = property 38 | _refresh() 39 | 40 | func _refresh(): 41 | if _property: 42 | var value = _property.get_default_value() as Array 43 | array_info.text = str(value.size()) + " Entries" 44 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_editor.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dinmptyyimoey 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_editor.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://cikbl461v071j"] 2 | 3 | [ext_resource type="Script" uid="uid://dinmptyyimoey" path="res://addons/pandora/ui/components/array_editor/array_editor.gd" id="1_yrssj"] 4 | [ext_resource type="Texture2D" uid="uid://b0mt8ysdc5m4a" path="res://addons/pandora/icons/Edit.svg" id="2_rmn70"] 5 | [ext_resource type="Script" uid="uid://b83eebnfe6lis" path="res://addons/pandora/ui/components/array_editor/array_window.gd" id="3_evj6f"] 6 | [ext_resource type="PackedScene" uid="uid://d15ldap7ubifc" path="res://addons/pandora/ui/components/array_editor/array_manager.tscn" id="4_5yyg5"] 7 | 8 | [node name="ArrayEditor" type="HBoxContainer"] 9 | size_flags_horizontal = 3 10 | size_flags_vertical = 3 11 | script = ExtResource("1_yrssj") 12 | 13 | [node name="ArrayInfo" type="LineEdit" parent="."] 14 | layout_mode = 2 15 | size_flags_horizontal = 3 16 | text = "0 Entries" 17 | editable = false 18 | context_menu_enabled = false 19 | virtual_keyboard_enabled = false 20 | shortcut_keys_enabled = false 21 | selecting_enabled = false 22 | flat = true 23 | 24 | [node name="EditArrayButton" type="Button" parent="."] 25 | layout_mode = 2 26 | icon = ExtResource("2_rmn70") 27 | 28 | [node name="ArrayWindow" type="Window" parent="."] 29 | disable_3d = true 30 | title = "Array Manager" 31 | initial_position = 2 32 | size = Vector2i(134, 100) 33 | visible = false 34 | wrap_controls = true 35 | content_scale_mode = 1 36 | content_scale_aspect = 4 37 | script = ExtResource("3_evj6f") 38 | 39 | [node name="ArrayManager" parent="ArrayWindow" instance=ExtResource("4_5yyg5")] 40 | 41 | [connection signal="close_requested" from="ArrayWindow" to="ArrayWindow" method="_on_close_requested"] 42 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_item.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | signal item_removal_requested 5 | 6 | var _property: PandoraProperty 7 | var _control: PandoraPropertyControl: 8 | set(c): 9 | _control = c 10 | _refresh_value.call_deferred() 11 | var _backend: PandoraEntityBackend 12 | 13 | @onready var item_value: MarginContainer = %ItemValue 14 | @onready var delete_item_button: Button = %DeleteItemButton 15 | @onready var confirmation_dialog: ConfirmationDialog = %ConfirmationDialog 16 | 17 | 18 | func init( 19 | property: PandoraProperty, control: PandoraPropertyControl, backend: PandoraEntityBackend 20 | ) -> void: 21 | if self._control != null: 22 | _control.queue_free() 23 | self._property = property 24 | self._control = control 25 | self._backend = backend 26 | 27 | 28 | func _ready(): 29 | delete_item_button.pressed.connect(func(): confirmation_dialog.popup()) 30 | confirmation_dialog.confirmed.connect(_delete_item) 31 | _refresh.call_deferred() 32 | if _control != null: 33 | _control.property_value_changed.connect(_refresh) 34 | 35 | 36 | func _refresh_value() -> void: 37 | for child in item_value.get_children(): 38 | child.queue_free() 39 | item_value.get_children().clear() 40 | item_value.add_child(_control) 41 | 42 | 43 | func _refresh(_value: Variant = null): 44 | if _control == null or _property == null: 45 | return 46 | _control.refresh() 47 | 48 | 49 | func _delete_item(): 50 | item_removal_requested.emit() 51 | queue_free() 52 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_item.gd.uid: -------------------------------------------------------------------------------- 1 | uid://csaisqucgg6bx 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_item.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://crcu03fp7fkyd"] 2 | 3 | [ext_resource type="Script" uid="uid://csaisqucgg6bx" path="res://addons/pandora/ui/components/array_editor/array_item.gd" id="1_3q6kl"] 4 | [ext_resource type="Texture2D" uid="uid://b2s1ixfakdj1e" path="res://addons/pandora/icons/Remove.svg" id="2_rgeed"] 5 | 6 | [node name="ArrayItem" type="PanelContainer"] 7 | size_flags_horizontal = 3 8 | size_flags_vertical = 3 9 | script = ExtResource("1_3q6kl") 10 | 11 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 12 | layout_mode = 2 13 | 14 | [node name="ItemValue" type="MarginContainer" parent="HBoxContainer"] 15 | unique_name_in_owner = true 16 | layout_mode = 2 17 | size_flags_horizontal = 3 18 | 19 | [node name="DeleteItemButton" type="Button" parent="HBoxContainer"] 20 | unique_name_in_owner = true 21 | layout_mode = 2 22 | icon = ExtResource("2_rgeed") 23 | 24 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] 25 | unique_name_in_owner = true 26 | initial_position = 2 27 | size = Vector2i(400, 200) 28 | dialog_text = "Are you sure you want to remove this item?" 29 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_manager.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | const ArrayType = preload("res://addons/pandora/model/types/array.gd") 5 | const ArrayItem = preload("res://addons/pandora/ui/components/array_editor/array_item.tscn") 6 | const PropertyBarScene = "res://addons/pandora/ui/components/property_bar/property_bar.tscn" 7 | 8 | signal item_added(item: Variant) 9 | signal item_removed(item: Variant) 10 | signal item_updated(idx: int, new_item: Variant) 11 | signal close_requested 12 | 13 | @onready var close_button: Button = %CloseButton 14 | @onready var new_item_button: Button = %NewItemButton 15 | @onready var items_container: VBoxContainer = %ArrayItems 16 | @onready var main_container: VBoxContainer = %MainContainer 17 | var property_bar: Node 18 | 19 | var _items: Array 20 | var _property: PandoraProperty 21 | 22 | 23 | func _ready(): 24 | close_button.pressed.connect(func(): close_requested.emit()) 25 | new_item_button.pressed.connect(_add_new_item) 26 | 27 | 28 | func open(property: PandoraProperty): 29 | _property = property 30 | var property_bar_scene = load(PropertyBarScene) 31 | property_bar = property_bar_scene.instantiate() 32 | property_bar._ready() 33 | _load_items.call_deferred() 34 | 35 | 36 | func close(): 37 | _remove_empty_items() 38 | _clear() 39 | property_bar.queue_free() 40 | 41 | 42 | func is_empty(item: Variant): 43 | var array_type = _property.get_setting(ArrayType.SETTING_ARRAY_TYPE) 44 | if array_type == "reference": 45 | return item == null 46 | if array_type == "resource": 47 | return is_instance_valid(item) == false 48 | elif array_type == "string": 49 | return item == "" 50 | else: 51 | return false 52 | 53 | 54 | func _remove_empty_items(): 55 | for index in range(_items.size()): 56 | if is_empty(_items[index]): 57 | _items.erase(index) 58 | item_removed.emit(_items[index]) 59 | 60 | 61 | func _clear(): 62 | _items.clear() 63 | for child in items_container.get_children(): 64 | child.queue_free() 65 | items_container.get_children().clear() 66 | 67 | 68 | func _load_items(): 69 | _clear() 70 | _items = _property.get_default_value().duplicate() 71 | var array_type = _property.get_setting(ArrayType.SETTING_ARRAY_TYPE) 72 | for i in range(_items.size()): 73 | var control = ( 74 | property_bar.get_scene_by_type(array_type).instantiate() as PandoraPropertyControl 75 | ) 76 | var item_property = PandoraProperty.new("", "array_item", array_type) 77 | item_property._setting_overrides = _property._setting_overrides 78 | var value = _items[i] 79 | if array_type == "resource": 80 | value = load(value) 81 | elif array_type == "reference": 82 | if value is Dictionary: 83 | value = Pandora.get_entity(value["_entity_id"]) 84 | elif value is PandoraReference: 85 | value = value.get_entity() 86 | item_property.set_default_value(value) 87 | _add_property_control(control, item_property, i) 88 | 89 | 90 | func _add_new_item(): 91 | var array_type = _property.get_setting(ArrayType.SETTING_ARRAY_TYPE) 92 | var scene = property_bar.get_scene_by_type(array_type) 93 | var control = scene.instantiate() as PandoraPropertyControl 94 | var item_property = PandoraProperty.new( 95 | "", "array_item", _property.get_setting(ArrayType.SETTING_ARRAY_TYPE) 96 | ) 97 | item_property._setting_overrides = _property._setting_overrides 98 | _items.append(item_property.get_default_value()) 99 | _add_property_control(control, item_property, _items.size() - 1) 100 | item_added.emit(_items[_items.size() - 1]) 101 | 102 | 103 | func _add_property_control( 104 | control: PandoraPropertyControl, item_property: PandoraProperty, idx: int 105 | ): 106 | var item = ArrayItem.instantiate() 107 | 108 | control.init(item_property) 109 | 110 | control.property_value_changed.connect(func(value: Variant): item_updated.emit(idx, value)) 111 | 112 | item.item_removal_requested.connect( 113 | func(): item_removed.emit(control._property.get_default_value()) 114 | ) 115 | item.init(_property, control, Pandora._entity_backend) 116 | items_container.add_child(item) 117 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_manager.gd.uid: -------------------------------------------------------------------------------- 1 | uid://db4alrrhmduii 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_manager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://d15ldap7ubifc"] 2 | 3 | [ext_resource type="Script" uid="uid://db4alrrhmduii" path="res://addons/pandora/ui/components/array_editor/array_manager.gd" id="1_f23k5"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1xsek"] 6 | 7 | [node name="ArrayManager" type="PanelContainer"] 8 | self_modulate = Color(0, 0, 0, 1) 9 | anchors_preset = 15 10 | anchor_right = 1.0 11 | anchor_bottom = 1.0 12 | grow_horizontal = 2 13 | grow_vertical = 2 14 | theme_override_styles/panel = SubResource("StyleBoxFlat_1xsek") 15 | script = ExtResource("1_f23k5") 16 | 17 | [node name="MarginContainer" type="MarginContainer" parent="."] 18 | layout_mode = 2 19 | theme_override_constants/margin_left = 10 20 | theme_override_constants/margin_top = 10 21 | theme_override_constants/margin_right = 10 22 | theme_override_constants/margin_bottom = 10 23 | 24 | [node name="MainContainer" type="VBoxContainer" parent="MarginContainer"] 25 | unique_name_in_owner = true 26 | layout_mode = 2 27 | 28 | [node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/MainContainer"] 29 | layout_mode = 2 30 | size_flags_vertical = 3 31 | 32 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MainContainer/ScrollContainer"] 33 | layout_mode = 2 34 | size_flags_horizontal = 3 35 | size_flags_vertical = 3 36 | 37 | [node name="HeaderContainer" type="MarginContainer" parent="MarginContainer/MainContainer/ScrollContainer/VBoxContainer"] 38 | layout_mode = 2 39 | size_flags_horizontal = 8 40 | theme_override_constants/margin_bottom = 10 41 | 42 | [node name="NewItemButton" type="Button" parent="MarginContainer/MainContainer/ScrollContainer/VBoxContainer/HeaderContainer"] 43 | unique_name_in_owner = true 44 | layout_mode = 2 45 | text = "Add New Item +" 46 | 47 | [node name="ArrayItems" type="VBoxContainer" parent="MarginContainer/MainContainer/ScrollContainer/VBoxContainer"] 48 | unique_name_in_owner = true 49 | layout_mode = 2 50 | 51 | [node name="CenterContainer" type="CenterContainer" parent="MarginContainer/MainContainer"] 52 | layout_mode = 2 53 | 54 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MainContainer/CenterContainer"] 55 | layout_mode = 2 56 | theme_override_constants/separation = 20 57 | 58 | [node name="CloseButton" type="Button" parent="MarginContainer/MainContainer/CenterContainer/HBoxContainer"] 59 | unique_name_in_owner = true 60 | layout_mode = 2 61 | text = "Close" 62 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_window.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Window 3 | 4 | signal item_added(item: Variant) 5 | signal item_removed(item: Variant) 6 | signal item_updated(idx: int, new_item: Variant) 7 | 8 | @onready var array_manager = $ArrayManager 9 | 10 | 11 | func _ready(): 12 | if owner.get_parent() is SubViewport: 13 | return 14 | array_manager.close_requested.connect(_on_close_requested) 15 | array_manager.item_added.connect(func(item: Variant): item_added.emit(item)) 16 | array_manager.item_removed.connect(func(item: Variant): item_removed.emit(item)) 17 | array_manager.item_updated.connect(func(idx: int, item: Variant): item_updated.emit(idx, item)) 18 | 19 | 20 | func open(property: PandoraProperty): 21 | popup_centered_clamped(Vector2i(800, 1000), 0.5) 22 | move_to_foreground() 23 | grab_focus() 24 | array_manager.open(property) 25 | 26 | 27 | func _on_close_requested(): 28 | hide() 29 | array_manager.close() 30 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/array_editor/array_window.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b83eebnfe6lis 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/color_picker/color_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MarginContainer 3 | 4 | @onready var color_picker_button: ColorPickerButton = $ColorPickerButton 5 | 6 | signal color_selected(color: Color) 7 | 8 | 9 | func _ready(): 10 | color_picker_button.color_changed.connect(func(color): color_selected.emit(color)) 11 | 12 | 13 | func set_color(color: Color) -> void: 14 | color_picker_button.color = color 15 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/color_picker/color_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://wnr5jjkir4j2 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/color_picker/color_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cex30s5jawlql"] 2 | 3 | [ext_resource type="Script" uid="uid://wnr5jjkir4j2" path="res://addons/pandora/ui/components/color_picker/color_picker.gd" id="1_eeppq"] 4 | 5 | [node name="ColorPicker" type="MarginContainer"] 6 | offset_right = 8.0 7 | offset_bottom = 55.0 8 | size_flags_horizontal = 3 9 | size_flags_vertical = 3 10 | script = ExtResource("1_eeppq") 11 | 12 | [node name="ColorPickerButton" type="ColorPickerButton" parent="."] 13 | layout_mode = 2 14 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_attributes/entity_attributes.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends VBoxContainer 3 | 4 | @onready var texture_picker = %TexturePicker 5 | @onready var script_picker = %ScriptPicker 6 | @onready var id_generation_enabled = %IdGenerationEnabled 7 | @onready var class_name_edit = %ClassNameEdit 8 | @onready var color_picker = %ColorPicker 9 | 10 | @onready var script_attribute = $ScriptAttribute 11 | @onready var h_separator_2 = $HSeparator2 12 | @onready var id_generation_attribute = $IdGenerationAttribute 13 | @onready var id_class_name_attribute = %IdClassNameAttribute 14 | 15 | var _entity: PandoraEntity 16 | 17 | 18 | func init(entity: PandoraEntity) -> void: 19 | self._entity = entity 20 | texture_picker.set_texture_path(entity.get_icon_path()) 21 | script_picker.set_script_path(entity.get_script_path()) 22 | color_picker.set_color(entity.get_icon_color()) 23 | # ensure selected script extends PandoraEntity! 24 | script_picker.set_filter(_is_entity) 25 | class_name_edit.text = entity.get_id_generation_class() 26 | id_generation_enabled.button_pressed = entity._generate_ids 27 | class_name_edit.editable = entity._generate_ids 28 | 29 | # only show script & id generation on categories 30 | script_attribute.visible = entity is PandoraCategory 31 | h_separator_2.visible = entity is PandoraCategory 32 | id_generation_attribute.visible = entity is PandoraCategory 33 | id_class_name_attribute.visible = entity is PandoraCategory 34 | 35 | 36 | func _ready() -> void: 37 | texture_picker.texture_changed.connect(_set_icon_path) 38 | script_picker.script_path_changed.connect(_set_script_path) 39 | id_generation_enabled.toggled.connect(_set_id_generation) 40 | class_name_edit.text_changed.connect(_set_class_name) 41 | color_picker.color_selected.connect(_set_icon_color) 42 | 43 | 44 | func _set_icon_path(path: String) -> void: 45 | _entity.set_icon_path(path) 46 | 47 | 48 | func _set_icon_color(color: Color) -> void: 49 | _entity.set_icon_color(color) 50 | 51 | 52 | func _set_script_path(path: String) -> void: 53 | _entity.set_script_path(path) 54 | 55 | 56 | func _set_id_generation(toggled: bool) -> void: 57 | _entity.set_generate_ids(toggled) 58 | class_name_edit.editable = toggled 59 | 60 | 61 | func _set_class_name(name: String) -> void: 62 | _entity.set_id_generation_class(name.to_pascal_case()) 63 | 64 | 65 | func _is_entity(path: String) -> bool: 66 | var EntityClass = load(path) 67 | if not EntityClass: 68 | return false 69 | var entity = EntityClass.new("", "", "", "") 70 | return (entity as PandoraEntity) != null 71 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_attributes/entity_attributes.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d3wqprwajfpi3 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_attributes/entity_attributes.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://ceqq28yvnhs2e"] 2 | 3 | [ext_resource type="Script" uid="uid://d3wqprwajfpi3" path="res://addons/pandora/ui/components/entity_attributes/entity_attributes.gd" id="1_hnn1r"] 4 | [ext_resource type="PackedScene" uid="uid://dvoxop0o2mlfi" path="res://addons/pandora/ui/components/texture_picker/texture_picker.tscn" id="2_tv6yu"] 5 | [ext_resource type="PackedScene" uid="uid://bp0gaqyb10bns" path="res://addons/pandora/ui/components/script_picker/script_picker.tscn" id="3_e2peg"] 6 | [ext_resource type="PackedScene" uid="uid://cex30s5jawlql" path="res://addons/pandora/ui/components/color_picker/color_picker.tscn" id="3_o3ybf"] 7 | 8 | [node name="EntityAttributes" type="VBoxContainer"] 9 | anchors_preset = 15 10 | anchor_right = 1.0 11 | anchor_bottom = 1.0 12 | grow_horizontal = 2 13 | grow_vertical = 2 14 | size_flags_horizontal = 3 15 | size_flags_vertical = 3 16 | script = ExtResource("1_hnn1r") 17 | 18 | [node name="HSeparator" type="HSeparator" parent="."] 19 | layout_mode = 2 20 | theme_override_constants/separation = 20 21 | 22 | [node name="IconAttribute" type="HBoxContainer" parent="."] 23 | layout_mode = 2 24 | 25 | [node name="IconLabel" type="Label" parent="IconAttribute"] 26 | layout_mode = 2 27 | size_flags_horizontal = 3 28 | text = "Icon" 29 | 30 | [node name="TexturePicker" parent="IconAttribute" instance=ExtResource("2_tv6yu")] 31 | unique_name_in_owner = true 32 | layout_mode = 2 33 | 34 | [node name="ColorAttribute" type="HBoxContainer" parent="."] 35 | layout_mode = 2 36 | 37 | [node name="ColorLabel" type="Label" parent="ColorAttribute"] 38 | layout_mode = 2 39 | size_flags_horizontal = 3 40 | text = "Color" 41 | 42 | [node name="ColorPicker" parent="ColorAttribute" instance=ExtResource("3_o3ybf")] 43 | unique_name_in_owner = true 44 | layout_mode = 2 45 | 46 | [node name="ScriptAttribute" type="HBoxContainer" parent="."] 47 | layout_mode = 2 48 | 49 | [node name="Label" type="Label" parent="ScriptAttribute"] 50 | layout_mode = 2 51 | size_flags_horizontal = 3 52 | text = "Script" 53 | 54 | [node name="ScriptPicker" parent="ScriptAttribute" instance=ExtResource("3_e2peg")] 55 | unique_name_in_owner = true 56 | layout_mode = 2 57 | 58 | [node name="HSeparator2" type="HSeparator" parent="."] 59 | layout_mode = 2 60 | theme_override_constants/separation = 20 61 | 62 | [node name="IdGenerationAttribute" type="HBoxContainer" parent="."] 63 | layout_mode = 2 64 | 65 | [node name="Label" type="Label" parent="IdGenerationAttribute"] 66 | layout_mode = 2 67 | size_flags_horizontal = 3 68 | text = "Generate ID Class" 69 | 70 | [node name="IdGenerationEnabled" type="CheckButton" parent="IdGenerationAttribute"] 71 | unique_name_in_owner = true 72 | layout_mode = 2 73 | 74 | [node name="IdClassNameAttribute" type="HBoxContainer" parent="."] 75 | unique_name_in_owner = true 76 | layout_mode = 2 77 | 78 | [node name="IdClassNameLabel" type="Label" parent="IdClassNameAttribute"] 79 | layout_mode = 2 80 | size_flags_horizontal = 3 81 | text = "ID Class Name" 82 | 83 | [node name="ClassNameEdit" type="LineEdit" parent="IdClassNameAttribute"] 84 | unique_name_in_owner = true 85 | layout_mode = 2 86 | size_flags_horizontal = 3 87 | placeholder_text = "SomeClassName" 88 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_picker/entity_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | 4 | signal entity_selected(entity: PandoraEntity) 5 | 6 | @onready var option_button = $OptionButton 7 | 8 | var hint_string: String 9 | var categories_only: bool: 10 | set(v): 11 | var old_value = categories_only 12 | categories_only = v 13 | if old_value != categories_only: 14 | _invalidate.call_deferred() 15 | 16 | var _ids_to_entities = {} 17 | var _entity_ids_to_ids = {} 18 | var _entities: Array[PandoraEntity] 19 | var _category_id_filter: String: 20 | set(v): 21 | var old_value = _category_id_filter 22 | _category_id_filter = v 23 | if old_value != _category_id_filter: 24 | _invalidate.call_deferred() 25 | var _sort: Callable = func(a, b): return false 26 | 27 | 28 | func _ready() -> void: 29 | option_button.get_popup().id_pressed.connect(_on_id_selected) 30 | _invalidate() 31 | 32 | 33 | func set_sort(sort: Callable) -> void: 34 | self._sort = sort 35 | _invalidate() 36 | 37 | 38 | func set_filter(category_id: String) -> void: 39 | self._category_id_filter = category_id 40 | _invalidate() 41 | 42 | 43 | func set_data(entities: Array[PandoraEntity]) -> void: 44 | self._entities = entities 45 | _ids_to_entities.clear() 46 | _entity_ids_to_ids.clear() 47 | var id_counter = 0 48 | option_button.get_popup().clear() 49 | for entity in _entities: 50 | option_button.get_popup().add_icon_item( 51 | load(entity.get_icon_path()), entity.get_entity_name(), id_counter 52 | ) 53 | 54 | var editor_plugin: EditorPlugin = ( 55 | Engine.get_meta("PandoraEditorPlugin") 56 | if Engine.has_meta("PandoraEditorPlugin") 57 | else null 58 | ) 59 | if editor_plugin: 60 | option_button.get_popup().set_item_icon_max_width( 61 | id_counter, editor_plugin.get_editor_interface().get_editor_scale() * 16 62 | ) 63 | # Godot 4.1+ 64 | if option_button.get_popup().has_method("set_item_icon_modulate"): 65 | option_button.get_popup().set_item_icon_modulate(id_counter, entity.get_icon_color()) 66 | _ids_to_entities[id_counter] = entity 67 | _entity_ids_to_ids[entity.get_entity_id()] = id_counter 68 | id_counter += 1 69 | 70 | 71 | func select(entity: PandoraEntity) -> void: 72 | var id = _entity_ids_to_ids[entity.get_entity_id()] 73 | option_button.select(id) 74 | option_button.modulate = entity.get_icon_color() 75 | 76 | 77 | func _on_id_selected(id: int) -> void: 78 | entity_selected.emit(_ids_to_entities[id]) 79 | 80 | 81 | func _invalidate() -> void: 82 | var filter = Pandora.get_category(_category_id_filter) if _category_id_filter else null 83 | if categories_only: 84 | set_data(Pandora.get_all_categories(filter, _sort)) 85 | else: 86 | set_data(Pandora.get_all_entities(filter, _sort)) 87 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_picker/entity_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://tv7m68jxrun4 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_picker/entity_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bfhqt0xa4a2fh"] 2 | 3 | [ext_resource type="Script" uid="uid://tv7m68jxrun4" path="res://addons/pandora/ui/components/entity_picker/entity_picker.gd" id="1_cjsnj"] 4 | 5 | [node name="EntityPicker" type="HBoxContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_cjsnj") 12 | 13 | [node name="OptionButton" type="OptionButton" parent="."] 14 | layout_mode = 2 15 | size_flags_horizontal = 3 16 | size_flags_vertical = 1 17 | expand_icon = true 18 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_tree/entity_tree.gd.uid: -------------------------------------------------------------------------------- 1 | uid://nrk4g58k31is 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/entity_tree/entity_tree.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://ctmsh7eg6wypu"] 2 | 3 | [ext_resource type="Script" uid="uid://nrk4g58k31is" path="res://addons/pandora/ui/components/entity_tree/entity_tree.gd" id="1_0uafu"] 4 | [ext_resource type="PackedScene" uid="uid://piqbfrsn5bi1" path="res://addons/pandora/ui/components/loading_spinner/LoadingSpinner.tscn" id="2_dokdi"] 5 | 6 | [node name="EntityTree" type="Tree"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | focus_mode = 1 13 | mouse_filter = 1 14 | hide_root = true 15 | script = ExtResource("1_0uafu") 16 | 17 | [node name="LoadingSpinner" parent="." instance=ExtResource("2_dokdi")] 18 | layout_mode = 1 19 | 20 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] 21 | initial_position = 2 22 | size = Vector2i(303, 80) 23 | dialog_text = "Are you sure you want to delete '%s'?" 24 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/loading_spinner/LoadingSpinner.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://piqbfrsn5bi1"] 2 | 3 | [ext_resource type="Script" uid="uid://pdchat0hnc06" path="res://addons/pandora/ui/components/loading_spinner/loading_spinner.gd" id="1_waqf6"] 4 | [ext_resource type="Texture2D" uid="uid://dmh7pik8vjog8" path="res://addons/pandora/icons/pandora-icon.svg" id="2_0ad3g"] 5 | 6 | [node name="LoadingSpinner" type="CenterContainer"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | script = ExtResource("1_waqf6") 13 | 14 | [node name="Control" type="Control" parent="."] 15 | custom_minimum_size = Vector2(64, 64) 16 | layout_mode = 2 17 | 18 | [node name="RotationSprite" type="Sprite2D" parent="Control"] 19 | position = Vector2(32, 32) 20 | rotation = -6.27916 21 | scale = Vector2(2, 2) 22 | texture = ExtResource("2_0ad3g") 23 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/loading_spinner/loading_spinner.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends CenterContainer 3 | 4 | @onready var rotation_sprite = $Control/RotationSprite 5 | 6 | 7 | func _ready() -> void: 8 | _tween() 9 | 10 | 11 | func _tween() -> void: 12 | var tween = create_tween() 13 | rotation_sprite.rotation_degrees = -360 14 | ( 15 | tween 16 | . tween_property(rotation_sprite, "rotation_degrees", 360, 1.5) 17 | . set_ease(Tween.EASE_IN_OUT) 18 | . set_trans(Tween.TRANS_CUBIC) 19 | . finished 20 | . connect(_tween) 21 | ) 22 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/loading_spinner/loading_spinner.gd.uid: -------------------------------------------------------------------------------- 1 | uid://pdchat0hnc06 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/notification_label/notification_label.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Label 3 | 4 | var tween: Tween 5 | 6 | 7 | func _ready() -> void: 8 | self_modulate.a = 0.0 9 | 10 | 11 | func popup() -> void: 12 | visible = true 13 | if tween and tween.is_running(): 14 | tween.stop() 15 | self_modulate.a = 1.0 16 | tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC) 17 | tween.finished.connect(func(): visible = false) 18 | tween.tween_property(self, "self_modulate:a", 0.0, 1.5).set_delay(2.0) 19 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/notification_label/notification_label.gd.uid: -------------------------------------------------------------------------------- 1 | uid://busm38g2lymg4 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/notification_label/notification_label.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dsqfuyjkcdwvu"] 2 | 3 | [ext_resource type="Script" uid="uid://busm38g2lymg4" path="res://addons/pandora/ui/components/notification_label/notification_label.gd" id="1_vp8r6"] 4 | 5 | [sub_resource type="LabelSettings" id="LabelSettings_ue3du"] 6 | line_spacing = 0.0 7 | font_color = Color(0.537255, 0.952941, 0, 1) 8 | outline_size = 2 9 | outline_color = Color(0, 0.866667, 0.541176, 1) 10 | shadow_size = 10 11 | shadow_color = Color(0, 0.627451, 0.772549, 0.14902) 12 | shadow_offset = Vector2(0, 0) 13 | 14 | [node name="SaveLabel" type="Label"] 15 | self_modulate = Color(1, 1, 1, 0) 16 | theme_override_colors/font_color = Color(0.564706, 0.92549, 0, 1) 17 | text = "Data saved!" 18 | label_settings = SubResource("LabelSettings_ue3du") 19 | script = ExtResource("1_vp8r6") 20 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/progress_bar/progress_bar.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name ImportProgressBar 3 | extends MarginContainer 4 | 5 | @onready var progress_bar = $ProgressBar 6 | 7 | var total_steps: int = 100 8 | 9 | 10 | # Called when the node enters the scene tree for the first time. 11 | func _ready(): 12 | visible = false 13 | 14 | 15 | func init(total_steps: int): 16 | total_steps = total_steps 17 | progress_bar.step = progress_bar.max_value / total_steps 18 | visible = true 19 | 20 | 21 | func advance(): 22 | progress_bar.value += progress_bar.step 23 | 24 | 25 | func finish(): 26 | total_steps = 100 27 | progress_bar.step = 0.01 28 | visible = false 29 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/progress_bar/progress_bar.gd.uid: -------------------------------------------------------------------------------- 1 | uid://sjpp7qx4vbjd 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/progress_bar/progress_bar.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dibfkp6i5uvgi"] 2 | 3 | [ext_resource type="Script" uid="uid://sjpp7qx4vbjd" path="res://addons/pandora/ui/components/progress_bar/progress_bar.gd" id="1_ifnan"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uy0ts"] 6 | bg_color = Color(0.278431, 0.701961, 0.360784, 1) 7 | 8 | [node name="ProgressContainer" type="MarginContainer"] 9 | visible = false 10 | offset_right = 100.0 11 | offset_bottom = 37.0 12 | theme_override_constants/margin_left = 5 13 | theme_override_constants/margin_top = 5 14 | theme_override_constants/margin_right = 5 15 | theme_override_constants/margin_bottom = 5 16 | script = ExtResource("1_ifnan") 17 | 18 | [node name="ProgressBar" type="ProgressBar" parent="."] 19 | layout_mode = 2 20 | theme_override_styles/fill = SubResource("StyleBoxFlat_uy0ts") 21 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/array/array_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | const ArrayType = preload("res://addons/pandora/model/types/array.gd") 5 | 6 | @onready var array_editor = $ArrayEditor 7 | var _items: Array = [] 8 | 9 | 10 | func _ready() -> void: 11 | refresh() 12 | _property.setting_changed.connect(_setting_changed) 13 | array_editor.item_added.connect(_on_item_added) 14 | array_editor.item_removed.connect(_on_item_removed) 15 | array_editor.item_updated.connect(_on_item_updated) 16 | 17 | 18 | func refresh() -> void: 19 | if _property != null: 20 | array_editor.set_property(_property) 21 | _items = _property.get_default_value().duplicate() 22 | 23 | 24 | func _on_item_added(item: Variant): 25 | _items.append(item) 26 | save_array() 27 | 28 | 29 | func _on_item_updated(idx: int, item: Variant): 30 | if range(_items.size()).has(idx): 31 | _items[idx] = item 32 | save_array() 33 | else: 34 | _on_item_added(item) 35 | 36 | 37 | func _on_item_removed(item: Variant): 38 | _items.erase(item) 39 | save_array() 40 | 41 | 42 | func save_array(): 43 | _property.set_default_value(_items) 44 | property_value_changed.emit(_items) 45 | 46 | 47 | func _setting_changed(key: String): 48 | if key == ArrayType.SETTING_ARRAY_TYPE: 49 | _items.clear() 50 | save_array() 51 | refresh() 52 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/array/array_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://btmowi7f4ffar 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/array/array_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://ckdfcxfes51ia"] 2 | 3 | [ext_resource type="Script" uid="uid://btmowi7f4ffar" path="res://addons/pandora/ui/components/properties/array/array_property.gd" id="1_ku3y1"] 4 | [ext_resource type="PackedScene" uid="uid://cikbl461v071j" path="res://addons/pandora/ui/components/array_editor/array_editor.tscn" id="2_j8x1r"] 5 | 6 | [node name="ArrayProperty" type="MarginContainer"] 7 | script = ExtResource("1_ku3y1") 8 | type = "array" 9 | 10 | [node name="ArrayEditor" parent="." instance=ExtResource("2_j8x1r")] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/bool/bool_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | @onready var check_button: CheckButton = $CheckButton 6 | 7 | 8 | func _ready() -> void: 9 | refresh() 10 | check_button.focus_exited.connect(func(): unfocused.emit()) 11 | check_button.focus_entered.connect(func(): focused.emit()) 12 | check_button.toggled.connect( 13 | func(toggled:bool): 14 | _property.set_default_value(toggled) 15 | property_value_changed.emit(toggled)) 16 | 17 | 18 | func refresh() -> void: 19 | check_button.set_pressed(_property.get_default_value() as bool) 20 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/bool/bool_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://yfs0hurvmhd1 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/bool/bool_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://brp6oodbm37gk"] 2 | 3 | [ext_resource type="Script" uid="uid://yfs0hurvmhd1" path="res://addons/pandora/ui/components/properties/bool/bool_property.gd" id="1_aee5b"] 4 | 5 | [node name="BoolProperty" type="MarginContainer"] 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_aee5b") 14 | type = "bool" 15 | 16 | [node name="CheckButton" type="CheckButton" parent="."] 17 | layout_mode = 2 18 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/color/color_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | @onready var color_picker_button: ColorPickerButton = $ColorPickerButton 6 | 7 | 8 | func _ready() -> void: 9 | refresh() 10 | color_picker_button.focus_exited.connect(func(): unfocused.emit()) 11 | color_picker_button.focus_entered.connect(func(): focused.emit()) 12 | color_picker_button.color_changed.connect( 13 | func(color:Color): 14 | _property.set_default_value(color) 15 | property_value_changed.emit(color)) 16 | 17 | 18 | func refresh() -> void: 19 | color_picker_button.color = _property.get_default_value() as Color 20 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/color/color_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cjpnsuvvhqlfc 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/color/color_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://chj1ateikyk5"] 2 | 3 | [ext_resource type="Script" uid="uid://cjpnsuvvhqlfc" path="res://addons/pandora/ui/components/properties/color/color_property.gd" id="1_tgtmt"] 4 | 5 | [node name="ColorProperty" type="MarginContainer"] 6 | offset_right = 8.0 7 | offset_bottom = 8.0 8 | size_flags_horizontal = 3 9 | size_flags_vertical = 3 10 | theme_override_constants/margin_left = 0 11 | theme_override_constants/margin_top = 0 12 | theme_override_constants/margin_right = 0 13 | theme_override_constants/margin_bottom = 0 14 | script = ExtResource("1_tgtmt") 15 | type = "color" 16 | 17 | [node name="ColorPickerButton" type="ColorPickerButton" parent="."] 18 | custom_minimum_size = Vector2(0, 55) 19 | layout_mode = 2 20 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/float/float_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | const FloatType = preload("res://addons/pandora/model/types/float.gd") 6 | 7 | 8 | @onready var spin_box: SpinBox = $SpinBox 9 | 10 | 11 | func _ready() -> void: 12 | if _property != null: 13 | _property.setting_changed.connect(_setting_changed) 14 | _property.setting_cleared.connect(_setting_changed) 15 | refresh() 16 | spin_box.focus_exited.connect(func(): unfocused.emit()) 17 | spin_box.focus_entered.connect(func(): focused.emit()) 18 | spin_box.value_changed.connect( 19 | func(value:float): 20 | _property.set_default_value(value) 21 | property_value_changed.emit(value)) 22 | 23 | 24 | func refresh() -> void: 25 | spin_box.rounded = false 26 | spin_box.value = _property.get_default_value() as float 27 | spin_box.min_value = _property.get_setting(FloatType.SETTING_MIN_VALUE) as int 28 | spin_box.max_value = _property.get_setting(FloatType.SETTING_MAX_VALUE) as int 29 | spin_box.step = _property.get_setting(FloatType.SETTING_STEPS) as float 30 | 31 | 32 | func _setting_changed(key:String) -> void: 33 | if key == FloatType.SETTING_MIN_VALUE || key == FloatType.SETTING_MAX_VALUE || key == FloatType.SETTING_STEPS: 34 | refresh() 35 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/float/float_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cmyen1e0ae0xx 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/float/float_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://75nkqyavb3aj"] 2 | 3 | [ext_resource type="Script" uid="uid://cmyen1e0ae0xx" path="res://addons/pandora/ui/components/properties/float/float_property.gd" id="1_1npi1"] 4 | 5 | [node name="FloatProperty" type="MarginContainer"] 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_1npi1") 14 | type = "float" 15 | 16 | [node name="SpinBox" type="SpinBox" parent="."] 17 | layout_mode = 2 18 | min_value = -1e+08 19 | max_value = 1e+08 20 | step = 0.01 21 | custom_arrow_step = 0.01 22 | select_all_on_focus = true 23 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/integer/integer_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | const IntType = preload("res://addons/pandora/model/types/int.gd") 6 | 7 | 8 | @onready var spin_box: SpinBox = $SpinBox 9 | 10 | 11 | func _ready() -> void: 12 | if _property != null: 13 | _property.setting_changed.connect(_setting_changed) 14 | _property.setting_cleared.connect(_setting_changed) 15 | refresh() 16 | spin_box.focus_entered.connect(func(): focused.emit()) 17 | spin_box.focus_exited.connect(func(): unfocused.emit()) 18 | spin_box.value_changed.connect( 19 | func(value:float): 20 | _property.set_default_value(int(value)) 21 | property_value_changed.emit(value)) 22 | 23 | 24 | func refresh() -> void: 25 | if _property != null: 26 | spin_box.value = _property.get_default_value() as int 27 | spin_box.min_value = _property.get_setting(IntType.SETTING_MIN_VALUE) as int 28 | spin_box.max_value = _property.get_setting(IntType.SETTING_MAX_VALUE) as int 29 | 30 | 31 | func _setting_changed(key:String) -> void: 32 | if key == IntType.SETTING_MIN_VALUE || key == IntType.SETTING_MAX_VALUE: 33 | refresh() 34 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/integer/integer_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://3dvks83yldd3 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/integer/integer_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://kgit41uva08d"] 2 | 3 | [ext_resource type="Script" uid="uid://3dvks83yldd3" path="res://addons/pandora/ui/components/properties/integer/integer_property.gd" id="1_82ywy"] 4 | 5 | [node name="IntegerProperty" type="MarginContainer"] 6 | size_flags_horizontal = 3 7 | size_flags_vertical = 3 8 | script = ExtResource("1_82ywy") 9 | type = "int" 10 | 11 | [node name="SpinBox" type="SpinBox" parent="."] 12 | layout_mode = 2 13 | focus_mode = 1 14 | max_value = 99999.0 15 | rounded = true 16 | select_all_on_focus = true 17 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/property_control.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PandoraPropertyControl extends MarginContainer 3 | 4 | signal property_value_changed(value: Variant) 5 | signal focused 6 | signal unfocused 7 | 8 | @export var type: String 9 | 10 | var _property: PandoraProperty 11 | 12 | 13 | func init(property: PandoraProperty) -> void: 14 | self._property = property 15 | 16 | 17 | func refresh() -> void: 18 | pass 19 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/property_control.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bvl8qiojdoihh 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/property_control_kvp.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | ## invoked when a property was selected that is inherited. 5 | signal inherited_property_selected(category_id: String, property_name: String) 6 | 7 | ## called when an original property was selected 8 | signal original_property_selected(property: PandoraProperty) 9 | 10 | var _property: PandoraProperty: 11 | set(p): 12 | _property = p 13 | _refresh_key.call_deferred() 14 | var _control: PandoraPropertyControl: 15 | set(c): 16 | _control = c 17 | _refresh_value.call_deferred() 18 | var _backend: PandoraEntityBackend 19 | 20 | @onready var property_key: LinkButton = %PropertyKey 21 | @onready var property_key_edit: LineEdit = %PropertyKeyEdit 22 | @onready var property_value: MarginContainer = %PropertyValue 23 | @onready var reset_button: Button = %ResetButton 24 | @onready var regenerate_id_button: Button = %RegenerateIDButton 25 | @onready var delete_property_button: Button = %DeletePropertyButton 26 | @onready var confirmation_dialog = %ConfirmationDialog 27 | 28 | 29 | func init( 30 | property: PandoraProperty, control: PandoraPropertyControl, backend: PandoraEntityBackend 31 | ) -> void: 32 | if self._control != null: 33 | _control.queue_free() 34 | self._property = property 35 | self._control = control 36 | self._backend = backend 37 | 38 | 39 | func _ready() -> void: 40 | property_key_edit.focus_entered.connect(_property_key_focused) 41 | property_key_edit.text_changed.connect(_property_name_changed) 42 | reset_button.pressed.connect(_property_reset_to_default) 43 | regenerate_id_button.pressed.connect(func(): Pandora.regenerate_property_id(_property)) 44 | delete_property_button.pressed.connect(func(): confirmation_dialog.popup()) 45 | confirmation_dialog.confirmed.connect(_delete_property) 46 | _refresh.call_deferred() 47 | if _property != null: 48 | _set_edit_name_mode(_property.is_original()) 49 | property_key.pressed.connect( 50 | func(): inherited_property_selected.emit( 51 | _property.get_original_category_id(), _property.get_property_name() 52 | ) 53 | ) 54 | if _control != null: 55 | _control.property_value_changed.connect(_refresh) 56 | 57 | 58 | func edit_key(): 59 | if property_key_edit.visible: 60 | property_key_edit.grab_focus() 61 | 62 | 63 | func _refresh_key() -> void: 64 | property_key.text = _property.get_property_name() 65 | property_key_edit.text = _property.get_property_name() 66 | 67 | 68 | func _refresh_value() -> void: 69 | for child in property_value.get_children(): 70 | child.queue_free() 71 | property_value.get_children().clear() 72 | property_value.add_child(_control) 73 | 74 | 75 | func _set_edit_name_mode(edit_mode: bool) -> void: 76 | property_key.visible = not edit_mode 77 | property_key_edit.visible = edit_mode 78 | 79 | 80 | func _property_name_changed(new_name: String) -> void: 81 | # FIXME avoid key duplication issue 82 | _property._name = new_name 83 | 84 | 85 | func _property_reset_to_default() -> void: 86 | _property.reset_to_default() 87 | _refresh() 88 | 89 | 90 | func _refresh(_value: Variant = null) -> void: 91 | if _control == null or _property == null: 92 | return 93 | # FIXME: focused & unfocused signals don't quite work! 94 | if not _control.unfocused.is_connected(_control_value_unfocused): 95 | _control.unfocused.connect(_control_value_unfocused) 96 | if not _control.focused.is_connected(_control_value_focused): 97 | _control.focused.connect(_control_value_focused) 98 | _control.refresh() 99 | reset_button.visible = not _property.is_original() and _property.is_overridden() 100 | regenerate_id_button.disabled = not _property.is_original() 101 | regenerate_id_button.visible = _property.is_original() 102 | if regenerate_id_button.disabled: 103 | regenerate_id_button.tooltip_text = "Inherited property id cannot be regenerated" 104 | else: 105 | regenerate_id_button.tooltip_text = "Regenerate property id" 106 | delete_property_button.disabled = not _property.is_original() 107 | delete_property_button.visible = _property.is_original() 108 | if delete_property_button.disabled: 109 | delete_property_button.tooltip_text = "Inherited property cannot be deleted" 110 | else: 111 | delete_property_button.tooltip_text = "Delete property" 112 | 113 | 114 | func _delete_property() -> void: 115 | _backend.delete_property(_property) 116 | queue_free() 117 | 118 | 119 | func _property_key_focused() -> void: 120 | original_property_selected.emit(_property) 121 | 122 | 123 | func _property_key_unfocused() -> void: 124 | pass 125 | 126 | 127 | func _control_value_focused() -> void: 128 | if property_key_edit.visible: 129 | original_property_selected.emit(_property) 130 | 131 | 132 | func _control_value_unfocused() -> void: 133 | pass 134 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/property_control_kvp.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ba0d3chmbcm8i 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/property_control_kvp.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://tgaigoiiqvmc"] 2 | 3 | [ext_resource type="Script" uid="uid://ba0d3chmbcm8i" path="res://addons/pandora/ui/components/properties/property_control_kvp.gd" id="1_1h35p"] 4 | [ext_resource type="Texture2D" uid="uid://bhcep67ihojnd" path="res://addons/pandora/icons/Reload.svg" id="2_718d2"] 5 | [ext_resource type="Texture2D" uid="uid://bfs83ic84umkv" path="res://addons/pandora/icons/nanoid.svg" id="3_1psa3"] 6 | [ext_resource type="Texture2D" uid="uid://b2s1ixfakdj1e" path="res://addons/pandora/icons/Remove.svg" id="4_2n76s"] 7 | 8 | [node name="PropertyControlKVP" type="PanelContainer"] 9 | size_flags_horizontal = 3 10 | size_flags_vertical = 3 11 | script = ExtResource("1_1h35p") 12 | 13 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 14 | layout_mode = 2 15 | 16 | [node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"] 17 | layout_mode = 2 18 | size_flags_horizontal = 3 19 | 20 | [node name="Container" type="HBoxContainer" parent="HBoxContainer/HBoxContainer"] 21 | layout_mode = 2 22 | size_flags_horizontal = 3 23 | size_flags_stretch_ratio = 2.0 24 | 25 | [node name="PropertyKey" type="LinkButton" parent="HBoxContainer/HBoxContainer/Container"] 26 | unique_name_in_owner = true 27 | visible = false 28 | layout_mode = 2 29 | size_flags_horizontal = 3 30 | 31 | [node name="PropertyKeyEdit" type="LineEdit" parent="HBoxContainer/HBoxContainer/Container"] 32 | unique_name_in_owner = true 33 | visible = false 34 | layout_mode = 2 35 | size_flags_horizontal = 3 36 | 37 | [node name="MarginContainer" type="MarginContainer" parent="HBoxContainer/HBoxContainer"] 38 | layout_mode = 2 39 | theme_override_constants/margin_right = -2 40 | 41 | [node name="ResetButton" type="Button" parent="HBoxContainer/HBoxContainer/MarginContainer"] 42 | unique_name_in_owner = true 43 | visible = false 44 | layout_mode = 2 45 | icon = ExtResource("2_718d2") 46 | 47 | [node name="PropertyValue" type="MarginContainer" parent="HBoxContainer"] 48 | unique_name_in_owner = true 49 | layout_mode = 2 50 | size_flags_horizontal = 3 51 | 52 | [node name="RegenerateIDButton" type="Button" parent="HBoxContainer"] 53 | unique_name_in_owner = true 54 | layout_mode = 2 55 | disabled = true 56 | icon = ExtResource("3_1psa3") 57 | 58 | [node name="DeletePropertyButton" type="Button" parent="HBoxContainer"] 59 | unique_name_in_owner = true 60 | layout_mode = 2 61 | disabled = true 62 | icon = ExtResource("4_2n76s") 63 | 64 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] 65 | unique_name_in_owner = true 66 | title = "Delete confirmation" 67 | initial_position = 4 68 | size = Vector2i(400, 200) 69 | dialog_text = "Confirm deletion? This can have impact on children!" 70 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/reference/reference_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | const ReferenceType = preload("res://addons/pandora/model/types/reference.gd") 6 | 7 | 8 | @onready var entity_picker = $EntityPicker 9 | 10 | 11 | func _ready() -> void: 12 | refresh() 13 | _property.setting_changed.connect(_setting_changed) 14 | _property.setting_cleared.connect(_setting_changed) 15 | entity_picker.focus_exited.connect(func(): unfocused.emit()) 16 | entity_picker.focus_entered.connect(func(): focused.emit()) 17 | entity_picker.entity_selected.connect( 18 | func(entity:PandoraEntity): 19 | _property.set_default_value(entity) 20 | property_value_changed.emit(entity)) 21 | 22 | 23 | func refresh() -> void: 24 | if _property != null: 25 | entity_picker.set_filter(_property.get_setting(ReferenceType.SETTING_CATEGORY_FILTER) as String) 26 | entity_picker.categories_only = _property.get_setting(ReferenceType.SETTING_CATEGORIES_ONLY) as bool 27 | match _property.get_setting(ReferenceType.SETTING_SORT_LIST) as String: 28 | ReferenceType.SORT_ALPHABETICALLY: 29 | entity_picker.set_sort(func(a,b): return a.get_entity_name() < b.get_entity_name()) 30 | ReferenceType.SORT_AS_IS: 31 | entity_picker.set_sort(func(a,b): return false) 32 | var entity = _property.get_default_value() as PandoraEntity 33 | if entity != null: 34 | entity_picker.select.call_deferred(entity) 35 | 36 | 37 | func _setting_changed(key:String) -> void: 38 | if key == ReferenceType.SETTING_CATEGORIES_ONLY || key == ReferenceType.SETTING_CATEGORY_FILTER || key == ReferenceType.SETTING_SORT_LIST: 39 | refresh() 40 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/reference/reference_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dnk1kd1cqhqqs 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/reference/reference_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://b22vuf1tui0q3"] 2 | 3 | [ext_resource type="Script" uid="uid://dnk1kd1cqhqqs" path="res://addons/pandora/ui/components/properties/reference/reference_property.gd" id="1_euup8"] 4 | [ext_resource type="PackedScene" uid="uid://bfhqt0xa4a2fh" path="res://addons/pandora/ui/components/entity_picker/entity_picker.tscn" id="2_kp4gb"] 5 | 6 | [node name="ReferenceProperty" type="MarginContainer"] 7 | script = ExtResource("1_euup8") 8 | type = "reference" 9 | 10 | [node name="EntityPicker" parent="." instance=ExtResource("2_kp4gb")] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/resource/resource_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | @onready var resource_picker = $ResourcePicker 6 | 7 | 8 | func _ready() -> void: 9 | refresh() 10 | resource_picker.focus_exited.connect(func(): unfocused.emit()) 11 | resource_picker.focus_entered.connect(func(): focused.emit()) 12 | resource_picker.resource_changed.connect( 13 | func(resource_path:String): 14 | _property.set_default_value(load(resource_path)) 15 | property_value_changed.emit(resource_path)) 16 | 17 | 18 | func refresh() -> void: 19 | if _property != null: 20 | var default_value = _property.get_default_value() as Resource 21 | if default_value != null: 22 | resource_picker.set_resource_path(default_value.resource_path) 23 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/resource/resource_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cx2h6mbdbc1h8 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/resource/resource_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://yhwimtiw711n"] 2 | 3 | [ext_resource type="Script" uid="uid://cx2h6mbdbc1h8" path="res://addons/pandora/ui/components/properties/resource/resource_property.gd" id="1_j84fe"] 4 | [ext_resource type="PackedScene" uid="uid://bvg3w88lp8uuc" path="res://addons/pandora/ui/components/resource_picker/resource_picker.tscn" id="2_5l2jx"] 5 | 6 | [node name="ResourceProperty" type="MarginContainer"] 7 | script = ExtResource("1_j84fe") 8 | type = "resource" 9 | 10 | [node name="ResourcePicker" parent="." instance=ExtResource("2_5l2jx")] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/string/string_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | @onready var line_edit: LineEdit = $LineEdit 6 | 7 | 8 | func _ready() -> void: 9 | refresh() 10 | line_edit.focus_exited.connect(func(): unfocused.emit()) 11 | line_edit.focus_entered.connect(func(): focused.emit()) 12 | line_edit.text_changed.connect( 13 | func(text:String): 14 | _property.set_default_value(text) 15 | property_value_changed.emit(text)) 16 | 17 | 18 | func refresh() -> void: 19 | if _property != null: 20 | var value = _property.get_default_value() as String 21 | if value != line_edit.text: 22 | line_edit.text = value 23 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/string/string_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://by1xtep440y1d 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/string/string_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://d02rt6sxqqha6"] 2 | 3 | [ext_resource type="Script" uid="uid://by1xtep440y1d" path="res://addons/pandora/ui/components/properties/string/string_property.gd" id="1_d17kv"] 4 | 5 | [node name="StringProperty" type="MarginContainer"] 6 | size_flags_horizontal = 3 7 | size_flags_vertical = 3 8 | script = ExtResource("1_d17kv") 9 | type = "string" 10 | 11 | [node name="LineEdit" type="LineEdit" parent="."] 12 | layout_mode = 2 13 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector2/vector2_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cmewrvvrbssm0"] 2 | 3 | [ext_resource type="Script" uid="uid://d31s8isfsqird" path="res://addons/pandora/ui/components/properties/vector/vector_property.gd" id="1_bd0jg"] 4 | 5 | [node name="Vector2Property" type="MarginContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_bd0jg") 12 | type = "vector2" 13 | 14 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 15 | layout_mode = 2 16 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector2i/vector2i_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://18iy7nw2r8gv"] 2 | 3 | [ext_resource type="Script" uid="uid://d31s8isfsqird" path="res://addons/pandora/ui/components/properties/vector/vector_property.gd" id="1_ticyw"] 4 | 5 | [node name="Vector2iProperty" type="MarginContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_ticyw") 12 | vector_type = 1 13 | type = "vector2i" 14 | 15 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 16 | layout_mode = 2 17 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector3/vector3_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cckh8r5sngw1m"] 2 | 3 | [ext_resource type="Script" uid="uid://d31s8isfsqird" path="res://addons/pandora/ui/components/properties/vector/vector_property.gd" id="1_jmmow"] 4 | 5 | [node name="Vector3Property" type="MarginContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_jmmow") 12 | vector_type = 2 13 | type = "vector3" 14 | 15 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 16 | layout_mode = 2 17 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector3i/vector3i_property.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://csevjm1e6lhlp"] 2 | 3 | [ext_resource type="Script" uid="uid://d31s8isfsqird" path="res://addons/pandora/ui/components/properties/vector/vector_property.gd" id="1_3onub"] 4 | 5 | [node name="Vector3iProperty" type="MarginContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_3onub") 12 | vector_type = 3 13 | type = "vector3i" 14 | 15 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 16 | layout_mode = 2 17 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector_property.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PandoraPropertyControl 3 | 4 | 5 | enum VectorType { VECTOR_2, VECTOR_2I, VECTOR_3, VECTOR_3I } 6 | 7 | 8 | const Vector2Type = preload("res://addons/pandora/model/types/vector2.gd") 9 | const Vector2iType = preload("res://addons/pandora/model/types/vector2i.gd") 10 | const Vector3Type = preload("res://addons/pandora/model/types/vector3.gd") 11 | const Vector3iType = preload("res://addons/pandora/model/types/vector3i.gd") 12 | 13 | 14 | @export var vector_type : VectorType 15 | 16 | 17 | @onready var hbox : HBoxContainer = $HBoxContainer 18 | 19 | 20 | var inputs : Array[SpinBox] = [] 21 | 22 | 23 | func _ready() -> void: 24 | var components : int 25 | match vector_type: 26 | VectorType.VECTOR_2, VectorType.VECTOR_2I: 27 | components = 2 28 | VectorType.VECTOR_3, VectorType.VECTOR_3I: 29 | components = 3 30 | for i in components: 31 | var input := _create_editor_spin_slider(i) 32 | inputs.append(input) 33 | hbox.add_child(input) 34 | if _property != null: 35 | _property.setting_changed.connect(_setting_changed) 36 | _property.setting_cleared.connect(_setting_changed) 37 | refresh() 38 | 39 | 40 | func refresh() -> void: 41 | if _property != null: 42 | var min_value : int 43 | var max_value : int 44 | var step := 1.0 45 | var value = _property.get_default_value() 46 | match vector_type: 47 | VectorType.VECTOR_2: 48 | min_value = _property.get_setting(Vector2Type.SETTING_MIN_COMPONENT_VALUE) as int 49 | max_value = _property.get_setting(Vector2Type.SETTING_MAX_COMPONENT_VALUE) as int 50 | step = _property.get_setting(Vector2Type.SETTING_STEPS) as float 51 | VectorType.VECTOR_2I: 52 | min_value = _property.get_setting(Vector2iType.SETTING_MIN_COMPONENT_VALUE) as int 53 | max_value = _property.get_setting(Vector2iType.SETTING_MAX_COMPONENT_VALUE) as int 54 | VectorType.VECTOR_3: 55 | min_value = _property.get_setting(Vector3Type.SETTING_MIN_COMPONENT_VALUE) as int 56 | max_value = _property.get_setting(Vector3Type.SETTING_MAX_COMPONENT_VALUE) as int 57 | step = _property.get_setting(Vector3Type.SETTING_STEPS) as float 58 | VectorType.VECTOR_3I: 59 | min_value = _property.get_setting(Vector3iType.SETTING_MIN_COMPONENT_VALUE) as int 60 | max_value = _property.get_setting(Vector3iType.SETTING_MAX_COMPONENT_VALUE) as int 61 | for i in inputs.size(): 62 | var input = inputs[i] 63 | input.min_value = min_value 64 | input.max_value = max_value 65 | input.step = step 66 | if value[i] != input.value: 67 | input.set_value_no_signal(value[i]) 68 | 69 | 70 | 71 | func _setting_changed(key:String) -> void: 72 | if (vector_type == VectorType.VECTOR_2 and 73 | (key == Vector2Type.SETTING_MIN_COMPONENT_VALUE or 74 | key == Vector2Type.SETTING_MAX_COMPONENT_VALUE or 75 | key == Vector2Type.SETTING_STEPS)) \ 76 | or \ 77 | (vector_type == VectorType.VECTOR_2I and 78 | (key == Vector2iType.SETTING_MIN_COMPONENT_VALUE or 79 | key == Vector2iType.SETTING_MAX_COMPONENT_VALUE)) \ 80 | or \ 81 | (vector_type == VectorType.VECTOR_3 and 82 | (key == Vector3Type.SETTING_MIN_COMPONENT_VALUE or 83 | key == Vector3Type.SETTING_MAX_COMPONENT_VALUE or 84 | key == Vector3Type.SETTING_STEPS)) \ 85 | or \ 86 | (vector_type == VectorType.VECTOR_3I and 87 | (key == Vector3iType.SETTING_MIN_COMPONENT_VALUE or 88 | key == Vector3iType.SETTING_MAX_COMPONENT_VALUE)): 89 | refresh() 90 | 91 | 92 | func _create_editor_spin_slider(axis:int) -> SpinBox: 93 | var node := SpinBox.new() 94 | var label = ["x", "y", "z"][axis] 95 | node.size_flags_horizontal = Control.SIZE_EXPAND_FILL 96 | node.prefix = label 97 | if vector_type == VectorType.VECTOR_2 or vector_type == VectorType.VECTOR_3: 98 | node.step = 0.01 99 | node.focus_entered.connect(func(): focused.emit()) 100 | node.focus_exited.connect(func(): unfocused.emit()) 101 | node.value_changed.connect( 102 | func(v): 103 | var value = _property.get_default_value() 104 | value[axis] = v 105 | _property.set_default_value(value) 106 | property_value_changed.emit(value)) 107 | return node 108 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/properties/vector/vector_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d31s8isfsqird 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_bar/property_bar.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PandoraPropertyBar extends HBoxContainer 3 | 4 | signal property_added(scene: PackedScene) 5 | 6 | @onready var _buttons = get_children() 7 | 8 | var type_to_scene = {} 9 | 10 | 11 | func _ready() -> void: 12 | for button in _buttons: 13 | button.pressed.connect(_pressed.bind(button as PandoraPropertyButton)) 14 | var scene_instance = button.scene.instantiate() 15 | type_to_scene[scene_instance.type] = button.scene 16 | scene_instance.queue_free() 17 | 18 | 19 | func _pressed(button: PandoraPropertyButton) -> void: 20 | property_added.emit(button.scene) 21 | 22 | 23 | func get_scene_by_type(type: String) -> PackedScene: 24 | return type_to_scene[type] 25 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_bar/property_bar.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bxesr1w5ukygv 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_bar/property_bar.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=27 format=3 uid="uid://8namx0kxhw0"] 2 | 3 | [ext_resource type="Script" uid="uid://bxesr1w5ukygv" path="res://addons/pandora/ui/components/property_bar/property_bar.gd" id="1_0r2cp"] 4 | [ext_resource type="Texture2D" uid="uid://do5tkodyvid10" path="res://addons/pandora/icons/String.svg" id="1_4esni"] 5 | [ext_resource type="Texture2D" uid="uid://blvy22tu53qxy" path="res://addons/pandora/icons/int.svg" id="2_vpyx8"] 6 | [ext_resource type="Texture2D" uid="uid://cgupnims1vk7r" path="res://addons/pandora/icons/float.svg" id="3_d0hjc"] 7 | [ext_resource type="Script" uid="uid://crr6rlkqtg6we" path="res://addons/pandora/ui/components/property_bar/property_button.gd" id="3_kwfh5"] 8 | [ext_resource type="PackedScene" uid="uid://d02rt6sxqqha6" path="res://addons/pandora/ui/components/properties/string/string_property.tscn" id="4_noo0c"] 9 | [ext_resource type="Texture2D" uid="uid://mmkaghs6sbx4" path="res://addons/pandora/icons/bool.svg" id="4_wkogm"] 10 | [ext_resource type="Texture2D" uid="uid://c2738ylh13lsi" path="res://addons/pandora/icons/Color.svg" id="5_m2x7s"] 11 | [ext_resource type="PackedScene" uid="uid://kgit41uva08d" path="res://addons/pandora/ui/components/properties/integer/integer_property.tscn" id="6_bgkni"] 12 | [ext_resource type="PackedScene" uid="uid://75nkqyavb3aj" path="res://addons/pandora/ui/components/properties/float/float_property.tscn" id="8_7fghm"] 13 | [ext_resource type="PackedScene" uid="uid://brp6oodbm37gk" path="res://addons/pandora/ui/components/properties/bool/bool_property.tscn" id="10_jsvgc"] 14 | [ext_resource type="Texture2D" uid="uid://bk7a4jkyif178" path="res://addons/pandora/icons/Vector2i.svg" id="11_2g827"] 15 | [ext_resource type="PackedScene" uid="uid://18iy7nw2r8gv" path="res://addons/pandora/ui/components/properties/vector/vector2i/vector2i_property.tscn" id="11_wu1l4"] 16 | [ext_resource type="PackedScene" uid="uid://csevjm1e6lhlp" path="res://addons/pandora/ui/components/properties/vector/vector3i/vector3i_property.tscn" id="12_6vngj"] 17 | [ext_resource type="PackedScene" uid="uid://chj1ateikyk5" path="res://addons/pandora/ui/components/properties/color/color_property.tscn" id="12_xjj7h"] 18 | [ext_resource type="Texture2D" uid="uid://c6kohe0abjrrs" path="res://addons/pandora/icons/Vector3i.svg" id="13_26qay"] 19 | [ext_resource type="Texture2D" uid="uid://dcqltisjej0lu" path="res://addons/pandora/icons/Vector2.svg" id="13_c5fps"] 20 | [ext_resource type="PackedScene" uid="uid://b22vuf1tui0q3" path="res://addons/pandora/ui/components/properties/reference/reference_property.tscn" id="13_suig5"] 21 | [ext_resource type="Texture2D" uid="uid://dojpd3ptnta4m" path="res://addons/pandora/icons/Object.svg" id="13_y2aqa"] 22 | [ext_resource type="PackedScene" uid="uid://cmewrvvrbssm0" path="res://addons/pandora/ui/components/properties/vector/vector2/vector2_property.tscn" id="14_oowe1"] 23 | [ext_resource type="Texture2D" uid="uid://rwodit05tms7" path="res://addons/pandora/icons/AtlasTexture.svg" id="15_dus4l"] 24 | [ext_resource type="PackedScene" uid="uid://yhwimtiw711n" path="res://addons/pandora/ui/components/properties/resource/resource_property.tscn" id="16_qe711"] 25 | [ext_resource type="Texture2D" uid="uid://bdxvds1pxhqv6" path="res://addons/pandora/icons/Vector3.svg" id="17_bx0em"] 26 | [ext_resource type="Texture2D" uid="uid://66qnm42libnj" path="res://addons/pandora/icons/Array.svg" id="17_ebxvo"] 27 | [ext_resource type="PackedScene" uid="uid://cckh8r5sngw1m" path="res://addons/pandora/ui/components/properties/vector/vector3/vector3_property.tscn" id="18_oxp4m"] 28 | [ext_resource type="PackedScene" uid="uid://ckdfcxfes51ia" path="res://addons/pandora/ui/components/properties/array/array_property.tscn" id="18_sjnhs"] 29 | 30 | [node name="PropertyBar" type="HBoxContainer"] 31 | offset_right = 35.0 32 | offset_bottom = 33.0 33 | theme_override_constants/separation = 10 34 | script = ExtResource("1_0r2cp") 35 | 36 | [node name="AddTextPropertyButton" type="Button" parent="."] 37 | layout_mode = 2 38 | size_flags_horizontal = 0 39 | tooltip_text = "String property" 40 | icon = ExtResource("1_4esni") 41 | script = ExtResource("3_kwfh5") 42 | scene = ExtResource("4_noo0c") 43 | 44 | [node name="AddIntegerPropertyButton" type="Button" parent="."] 45 | layout_mode = 2 46 | size_flags_horizontal = 0 47 | tooltip_text = "Integer property" 48 | icon = ExtResource("2_vpyx8") 49 | script = ExtResource("3_kwfh5") 50 | scene = ExtResource("6_bgkni") 51 | 52 | [node name="AddFloatPropertyButton" type="Button" parent="."] 53 | layout_mode = 2 54 | size_flags_horizontal = 0 55 | tooltip_text = "Float property" 56 | icon = ExtResource("3_d0hjc") 57 | script = ExtResource("3_kwfh5") 58 | scene = ExtResource("8_7fghm") 59 | 60 | [node name="AddBoolPropertyButton" type="Button" parent="."] 61 | layout_mode = 2 62 | size_flags_horizontal = 0 63 | tooltip_text = "Bool property" 64 | icon = ExtResource("4_wkogm") 65 | script = ExtResource("3_kwfh5") 66 | scene = ExtResource("10_jsvgc") 67 | 68 | [node name="AddVector2iPropertyButton" type="Button" parent="."] 69 | layout_mode = 2 70 | tooltip_text = "Vector2i property" 71 | icon = ExtResource("11_2g827") 72 | script = ExtResource("3_kwfh5") 73 | scene = ExtResource("11_wu1l4") 74 | 75 | [node name="AddVector2PropertyButton" type="Button" parent="."] 76 | layout_mode = 2 77 | tooltip_text = "Vector2 property" 78 | icon = ExtResource("13_c5fps") 79 | script = ExtResource("3_kwfh5") 80 | scene = ExtResource("14_oowe1") 81 | 82 | [node name="AddVector3iPropertyButton" type="Button" parent="."] 83 | layout_mode = 2 84 | tooltip_text = "Vector3i property" 85 | icon = ExtResource("13_26qay") 86 | script = ExtResource("3_kwfh5") 87 | scene = ExtResource("12_6vngj") 88 | 89 | [node name="AddVector3PropertyButton" type="Button" parent="."] 90 | layout_mode = 2 91 | tooltip_text = "Vector3 property" 92 | icon = ExtResource("17_bx0em") 93 | script = ExtResource("3_kwfh5") 94 | scene = ExtResource("18_oxp4m") 95 | 96 | [node name="AddColorPropertyButton" type="Button" parent="."] 97 | layout_mode = 2 98 | size_flags_horizontal = 0 99 | tooltip_text = "Color property" 100 | icon = ExtResource("5_m2x7s") 101 | script = ExtResource("3_kwfh5") 102 | scene = ExtResource("12_xjj7h") 103 | 104 | [node name="AddReferencePropertyButton" type="Button" parent="."] 105 | layout_mode = 2 106 | size_flags_horizontal = 0 107 | tooltip_text = "Reference property" 108 | icon = ExtResource("13_y2aqa") 109 | script = ExtResource("3_kwfh5") 110 | scene = ExtResource("13_suig5") 111 | 112 | [node name="AddResourcePropertyButton" type="Button" parent="."] 113 | layout_mode = 2 114 | tooltip_text = "Resource property" 115 | icon = ExtResource("15_dus4l") 116 | script = ExtResource("3_kwfh5") 117 | scene = ExtResource("16_qe711") 118 | 119 | [node name="AddArrayPropertyButton" type="Button" parent="."] 120 | layout_mode = 2 121 | tooltip_text = "Array property" 122 | icon = ExtResource("17_ebxvo") 123 | script = ExtResource("3_kwfh5") 124 | scene = ExtResource("18_sjnhs") 125 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_bar/property_button.gd: -------------------------------------------------------------------------------- 1 | class_name PandoraPropertyButton extends Button 2 | 3 | @export var scene: PackedScene 4 | 5 | 6 | func _ready(): 7 | if scene: 8 | var scene_instance = scene.instantiate() 9 | var property_type = PandoraPropertyType.lookup(scene_instance.type) 10 | icon = load(property_type.get_type_icon_path()) 11 | scene_instance.queue_free() 12 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_bar/property_button.gd.uid: -------------------------------------------------------------------------------- 1 | uid://crr6rlkqtg6we 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_type_picker/property_type_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_type_picker/property_type_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://clt62qk6a21q4 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/property_type_picker/property_type_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://xbns62fkvuc7"] 2 | 3 | [ext_resource type="Script" uid="uid://clt62qk6a21q4" path="res://addons/pandora/ui/components/property_type_picker/property_type_picker.gd" id="1_7kqcl"] 4 | 5 | [node name="PropertyTypePicker" type="HBoxContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_7kqcl") 12 | 13 | [node name="OptionButton" type="OptionButton" parent="."] 14 | layout_mode = 2 15 | size_flags_horizontal = 3 16 | size_flags_vertical = 0 17 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/resource_picker/resource_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | 4 | signal resource_changed(resource_path: String) 5 | 6 | @onready var line_edit = $LineEdit 7 | @onready var load_file_button = $LoadFileButton 8 | @onready var file_dialog: FileDialog = $FileDialog 9 | 10 | var resource_path: String 11 | 12 | 13 | func _ready() -> void: 14 | line_edit.text_submitted.connect(_path_changed) 15 | load_file_button.pressed.connect(file_dialog.popup) 16 | file_dialog.file_selected.connect(_path_changed) 17 | 18 | 19 | func set_resource_path(path: String) -> void: 20 | var resource = load(path) as Resource 21 | if resource != null: 22 | line_edit.text = path 23 | self.resource_path = path 24 | 25 | 26 | func _path_changed(new_path: String) -> void: 27 | if new_path.begins_with("res://"): 28 | var resource = load(new_path) as Resource 29 | if resource != null and resource_path != new_path: 30 | resource_path = new_path 31 | resource_changed.emit(new_path) 32 | else: 33 | line_edit.text = resource_path 34 | 35 | 36 | func _can_drop_data(_pos, data): 37 | if data.type == "files": 38 | return true 39 | return false 40 | 41 | 42 | func _drop_data(_pos, data): 43 | if data.type == "files": 44 | var path = data.files[0] 45 | _path_changed(path) 46 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/resource_picker/resource_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://do7o7akn03aw3 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/resource_picker/resource_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bvg3w88lp8uuc"] 2 | 3 | [ext_resource type="Script" uid="uid://do7o7akn03aw3" path="res://addons/pandora/ui/components/resource_picker/resource_picker.gd" id="1_hjkiw"] 4 | [ext_resource type="Texture2D" uid="uid://dimpswbv6s8t2" path="res://addons/pandora/icons/Folder.svg" id="3_51u7n"] 5 | 6 | [node name="ResourcePicker" type="HBoxContainer"] 7 | size_flags_horizontal = 3 8 | size_flags_vertical = 3 9 | script = ExtResource("1_hjkiw") 10 | 11 | [node name="LineEdit" type="LineEdit" parent="."] 12 | layout_mode = 2 13 | size_flags_horizontal = 3 14 | mouse_filter = 1 15 | 16 | [node name="LoadFileButton" type="Button" parent="."] 17 | layout_mode = 2 18 | icon = ExtResource("3_51u7n") 19 | 20 | [node name="FileDialog" type="FileDialog" parent="."] 21 | title = "Open a File" 22 | initial_position = 2 23 | size = Vector2i(784, 324) 24 | ok_button_text = "Open" 25 | file_mode = 0 26 | filters = PackedStringArray("*.tres", "*.jpg", "*.png", "*.jpeg", "*.wav", "*.mp3", "*.ogg", "*.tscn", "*.mp4", "*.gif", "*.gd") 27 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/script_picker/script_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | 4 | signal script_path_changed(script_path: String) 5 | 6 | @onready var line_edit = $LineEdit 7 | @onready var button = $Button 8 | @onready var file_dialog = $FileDialog 9 | 10 | var _filter: Callable 11 | 12 | 13 | func _ready() -> void: 14 | line_edit.text_submitted.connect(_path_changed) 15 | button.pressed.connect(file_dialog.popup) 16 | file_dialog.file_selected.connect(_path_changed) 17 | 18 | 19 | func set_filter(filter: Callable) -> void: 20 | self._filter = filter 21 | 22 | 23 | func set_script_path(script_path: String) -> void: 24 | if line_edit.text != script_path: 25 | line_edit.text = script_path 26 | 27 | 28 | func _path_changed(new_path: String) -> void: 29 | if new_path.begins_with("res://"): 30 | if _filter == null or _filter.call(new_path): 31 | set_script_path(new_path) 32 | script_path_changed.emit(new_path) 33 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/script_picker/script_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cc3lh861dwdq3 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/script_picker/script_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bp0gaqyb10bns"] 2 | 3 | [ext_resource type="Script" uid="uid://cc3lh861dwdq3" path="res://addons/pandora/ui/components/script_picker/script_picker.gd" id="1_7xx4n"] 4 | [ext_resource type="Texture2D" uid="uid://dimpswbv6s8t2" path="res://addons/pandora/icons/Folder.svg" id="2_gmucu"] 5 | 6 | [node name="ScriptPicker" type="HBoxContainer"] 7 | size_flags_horizontal = 3 8 | script = ExtResource("1_7xx4n") 9 | 10 | [node name="LineEdit" type="LineEdit" parent="."] 11 | layout_mode = 2 12 | size_flags_horizontal = 3 13 | 14 | [node name="Button" type="Button" parent="."] 15 | layout_mode = 2 16 | icon = ExtResource("2_gmucu") 17 | 18 | [node name="FileDialog" type="FileDialog" parent="."] 19 | title = "Open a File" 20 | initial_position = 2 21 | size = Vector2i(400, 324) 22 | ok_button_text = "Open" 23 | file_mode = 0 24 | filters = PackedStringArray("*.gd") 25 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/texture_picker/texture_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HBoxContainer 3 | 4 | signal texture_changed(texture_path: String) 5 | 6 | @onready var texture_rect = $TextureRect 7 | @onready var line_edit = $LineEdit 8 | @onready var load_file_button = $LoadFileButton 9 | @onready var file_dialog: FileDialog = $FileDialog 10 | 11 | var texture_path: String 12 | 13 | 14 | func _ready() -> void: 15 | line_edit.text_submitted.connect(_path_changed) 16 | load_file_button.pressed.connect(file_dialog.popup) 17 | file_dialog.file_selected.connect(_path_changed) 18 | 19 | 20 | func set_texture_path(path: String) -> void: 21 | var resource = load(path) as Texture2D 22 | if resource != null: 23 | texture_rect.texture = resource 24 | line_edit.text = path 25 | self.texture_path = path 26 | 27 | 28 | func _path_changed(new_path: String) -> void: 29 | if new_path.begins_with("res://"): 30 | var resource = load(new_path) as Texture2D 31 | if resource != null and texture_path != new_path: 32 | texture_path = new_path 33 | texture_rect.texture = resource 34 | texture_changed.emit(new_path) 35 | else: 36 | line_edit.text = texture_path 37 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/texture_picker/texture_picker.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bb0m1hfpcrgr8 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/texture_picker/texture_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dvoxop0o2mlfi"] 2 | 3 | [ext_resource type="Script" uid="uid://bb0m1hfpcrgr8" path="res://addons/pandora/ui/components/texture_picker/texture_picker.gd" id="1_dilp1"] 4 | [ext_resource type="Texture2D" uid="uid://dimpswbv6s8t2" path="res://addons/pandora/icons/Folder.svg" id="3_0esgd"] 5 | 6 | [node name="TexturePicker" type="HBoxContainer"] 7 | size_flags_horizontal = 3 8 | size_flags_vertical = 3 9 | script = ExtResource("1_dilp1") 10 | 11 | [node name="TextureRect" type="TextureRect" parent="."] 12 | layout_mode = 2 13 | expand_mode = 2 14 | 15 | [node name="LineEdit" type="LineEdit" parent="."] 16 | layout_mode = 2 17 | size_flags_horizontal = 3 18 | 19 | [node name="LoadFileButton" type="Button" parent="."] 20 | layout_mode = 2 21 | icon = ExtResource("3_0esgd") 22 | 23 | [node name="FileDialog" type="FileDialog" parent="."] 24 | title = "Open a File" 25 | initial_position = 2 26 | size = Vector2i(784, 324) 27 | ok_button_text = "Open" 28 | file_mode = 0 29 | filters = PackedStringArray("*.svg", "*.jpg", "*.png", "*.jpeg") 30 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/update_button/update_button.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Button 3 | 4 | const RELEASE_URL: String = "https://api.github.com/repos/bitbrain/pandora/releases" 5 | 6 | @onready var http_request: HTTPRequest = %HTTPRequest 7 | @onready var updater_window: AcceptDialog = $UpdaterWindow 8 | @onready var updater: Control = $UpdaterWindow/UpdaterControl 9 | @onready var post_update_window: ConfirmationDialog = $PostUpdateWindow 10 | 11 | 12 | func _ready() -> void: 13 | self.hide() 14 | check_for_updates() 15 | 16 | 17 | func get_version() -> String: 18 | var plugin_config: ConfigFile = ConfigFile.new() 19 | plugin_config.load("res://addons/pandora/plugin.cfg") 20 | return plugin_config.get_value("plugin", "version") 21 | 22 | 23 | func check_for_updates() -> void: 24 | http_request.request(RELEASE_URL) 25 | 26 | 27 | func version_to_number(ver: String) -> int: 28 | ver = ver.lstrip("v") 29 | ver = ver.split("+")[0] 30 | 31 | var parts = ver.split("-") 32 | var release_ver = parts[0] 33 | parts.remove_at(0) 34 | 35 | var phase = null 36 | if parts.size() > 0: 37 | phase = "-".join(parts) 38 | 39 | var nums = release_ver.split(".") 40 | var size = nums.size() 41 | var offset = 0 if phase == null else 1 42 | var value = 0 43 | 44 | for idx in range(size): 45 | var item = nums[idx] 46 | if item.is_valid_int(): 47 | value += item.to_int() * (100 ** (size + offset - idx)) 48 | 49 | # If the release is stable, add 75 to be greater than alpha, beta & rc 50 | if phase == null && idx == size - 1: 51 | value += 75 52 | 53 | # The lstrip is done seperately so it works even if the "." is not present 54 | if phase != null: 55 | if phase.begins_with("alpha"): 56 | value += phase.lstrip("alpha").lstrip(".").to_int() 57 | elif phase.begins_with("beta"): 58 | value += 25 + phase.lstrip("beta").lstrip(".").to_int() 59 | elif phase.begins_with("rc"): 60 | value += 50 + phase.lstrip("rc").lstrip(".").to_int() 61 | 62 | return value 63 | 64 | 65 | func _on_http_request_completed( 66 | result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray 67 | ) -> void: 68 | if result != HTTPRequest.RESULT_SUCCESS: 69 | return 70 | 71 | var res = JSON.parse_string(body.get_string_from_utf8()) 72 | if typeof(res) != TYPE_ARRAY: 73 | return 74 | 75 | var curr_version_num = version_to_number(get_version()) 76 | 77 | var new_versions: Array = (res as Array).filter( 78 | func(release): return version_to_number(release.tag_name) > curr_version_num 79 | ) 80 | 81 | if new_versions.size() > 0: 82 | self.show() 83 | updater.releases = new_versions 84 | 85 | 86 | func _on_update_button_pressed() -> void: 87 | updater_window.show() 88 | 89 | 90 | func _on_updater_update_done(success: bool) -> void: 91 | if success: 92 | post_update_window.set_text("Updated Pandora successfully!\nRestart editor?") 93 | else: 94 | post_update_window.set_text("Could not update Pandora!\nRestart editor?") 95 | 96 | post_update_window.show() 97 | 98 | 99 | func _on_post_update_window_confirmed() -> void: 100 | var plugin: EditorPlugin = Engine.get_meta("PandoraEditorPlugin") 101 | plugin.get_editor_interface().restart_editor(true) 102 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/update_button/update_button.gd.uid: -------------------------------------------------------------------------------- 1 | uid://sn8ptmm6u17a 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/update_button/update_button.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dy4xl20q2nq7q"] 2 | 3 | [ext_resource type="Script" uid="uid://sn8ptmm6u17a" path="res://addons/pandora/ui/components/update_button/update_button.gd" id="1_0iteh"] 4 | [ext_resource type="PackedScene" uid="uid://dtwiql0cpn5hu" path="res://addons/pandora/ui/components/updater/updater.tscn" id="2_lifbu"] 5 | 6 | [node name="UpdateButton" type="Button"] 7 | visible = false 8 | text = "Update" 9 | script = ExtResource("1_0iteh") 10 | 11 | [node name="HTTPRequest" type="HTTPRequest" parent="."] 12 | unique_name_in_owner = true 13 | 14 | [node name="UpdaterWindow" type="AcceptDialog" parent="."] 15 | title = "Update Pandora" 16 | initial_position = 2 17 | size = Vector2i(600, 450) 18 | unresizable = true 19 | ok_button_text = "Close" 20 | 21 | [node name="UpdaterControl" parent="UpdaterWindow" instance=ExtResource("2_lifbu")] 22 | 23 | [node name="PostUpdateWindow" type="ConfirmationDialog" parent="."] 24 | title = "Pandora Updater" 25 | initial_position = 2 26 | size = Vector2i(343, 109) 27 | ok_button_text = "Restart" 28 | dialog_text = "Updated Pandora successfully! 29 | Restart editor?" 30 | 31 | [connection signal="pressed" from="." to="." method="_on_update_button_pressed"] 32 | [connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_completed"] 33 | [connection signal="update_done" from="UpdaterWindow/UpdaterControl" to="." method="_on_updater_update_done"] 34 | [connection signal="confirmed" from="PostUpdateWindow" to="." method="_on_post_update_window_confirmed"] 35 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/updater/updater.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | signal update_done(success: bool) 5 | 6 | const TEMP_FILE_NAME: String = "user://temp.zip" 7 | 8 | @onready var http_request: HTTPRequest = $HTTPRequest 9 | @onready var release_text: TextEdit = $VBoxContainer/ReleaseText 10 | @onready var release_link: LinkButton = $VBoxContainer/CenterContainer2/LinkButton 11 | @onready var download_btn: Button = $VBoxContainer/CenterContainer/DownloadButton 12 | @onready var version_text: Label = $VBoxContainer/VersionText 13 | 14 | var releases: Array = []: 15 | set(value): 16 | releases = value 17 | _update_ui() 18 | get: 19 | return releases 20 | 21 | 22 | func _update_ui() -> void: 23 | if releases.is_empty(): 24 | return 25 | 26 | var release_notes_text = "" 27 | for release in releases: 28 | release_notes_text += "Release " + release.tag_name + "\n" + release.body + "\n\n" 29 | release_notes_text = release_notes_text.rstrip("\n") 30 | release_text.set_text(release_notes_text) 31 | 32 | version_text.set_text("Latest version: " + releases[0].tag_name) 33 | release_link.set_uri(releases[0].html_url) 34 | 35 | 36 | func _on_download_button_pressed() -> void: 37 | if releases.is_empty(): 38 | self.update_done.emit(false) 39 | self.hide() 40 | return 41 | 42 | if FileAccess.file_exists("res://examples/inventory/inventory_example.gd"): 43 | prints("You can't update the addon from within itself.") 44 | return 45 | 46 | http_request.request(releases[0].zipball_url) 47 | download_btn.disabled = true 48 | download_btn.set_text("Downloading") 49 | 50 | 51 | func _on_http_request_request_completed( 52 | result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray 53 | ) -> void: 54 | if result != HTTPRequest.RESULT_SUCCESS: 55 | self.update_done.emit(false) 56 | self.hide() 57 | return 58 | 59 | var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE) 60 | zip_file.store_buffer(body) 61 | zip_file.close() 62 | 63 | OS.move_to_trash(ProjectSettings.globalize_path("res://addons/pandora")) 64 | 65 | var zip_reader: ZIPReader = ZIPReader.new() 66 | zip_reader.open(TEMP_FILE_NAME) 67 | var files: PackedStringArray = zip_reader.get_files() 68 | 69 | var base_path = files[1] 70 | 71 | for path in files: 72 | var new_file_path: String = path.replace(base_path, "") 73 | if path.ends_with("/"): 74 | DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path) 75 | else: 76 | var file: FileAccess = FileAccess.open( 77 | "res://addons/%s" % new_file_path, FileAccess.WRITE 78 | ) 79 | file.store_buffer(zip_reader.read_file(path)) 80 | 81 | zip_reader.close() 82 | DirAccess.remove_absolute(TEMP_FILE_NAME) 83 | 84 | self.update_done.emit(true) 85 | self.hide() 86 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/updater/updater.gd.uid: -------------------------------------------------------------------------------- 1 | uid://df16diqheefu8 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/components/updater/updater.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dtwiql0cpn5hu"] 2 | 3 | [ext_resource type="Script" uid="uid://df16diqheefu8" path="res://addons/pandora/ui/components/updater/updater.gd" id="1_e18b4"] 4 | [ext_resource type="Texture2D" uid="uid://crgjwaubao8pj" path="res://addons/pandora/icons/icon.png" id="1_jpf8m"] 5 | 6 | [node name="UpdaterControl" type="Control"] 7 | layout_mode = 3 8 | anchors_preset = 15 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | offset_left = 8.0 12 | offset_top = 8.0 13 | offset_right = -8.0 14 | offset_bottom = -49.0 15 | grow_horizontal = 2 16 | grow_vertical = 2 17 | script = ExtResource("1_e18b4") 18 | 19 | [node name="HTTPRequest" type="HTTPRequest" parent="."] 20 | 21 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 22 | layout_mode = 1 23 | anchors_preset = 15 24 | anchor_right = 1.0 25 | anchor_bottom = 1.0 26 | grow_horizontal = 2 27 | grow_vertical = 2 28 | theme_override_constants/separation = 8 29 | 30 | [node name="ReleaseNotesText" type="Label" parent="VBoxContainer"] 31 | layout_mode = 2 32 | text = "Release Notes" 33 | 34 | [node name="ReleaseText" type="TextEdit" parent="VBoxContainer"] 35 | custom_minimum_size = Vector2(256, 256) 36 | layout_mode = 2 37 | editable = false 38 | context_menu_enabled = false 39 | shortcut_keys_enabled = false 40 | deselect_on_focus_loss_enabled = false 41 | drag_and_drop_selection_enabled = false 42 | virtual_keyboard_enabled = false 43 | middle_mouse_paste_enabled = false 44 | 45 | [node name="TextureRect" type="TextureRect" parent="VBoxContainer"] 46 | visible = false 47 | custom_minimum_size = Vector2(128, 128) 48 | layout_mode = 2 49 | texture = ExtResource("1_jpf8m") 50 | expand_mode = 1 51 | stretch_mode = 5 52 | 53 | [node name="VersionText" type="Label" parent="VBoxContainer"] 54 | layout_mode = 2 55 | text = "Latest version: vx.y.-alphaZ" 56 | horizontal_alignment = 1 57 | 58 | [node name="CenterContainer" type="CenterContainer" parent="VBoxContainer"] 59 | layout_mode = 2 60 | 61 | [node name="DownloadButton" type="Button" parent="VBoxContainer/CenterContainer"] 62 | layout_mode = 2 63 | text = "Download and install" 64 | 65 | [node name="CenterContainer2" type="CenterContainer" parent="VBoxContainer"] 66 | layout_mode = 2 67 | 68 | [node name="LinkButton" type="LinkButton" parent="VBoxContainer/CenterContainer2"] 69 | layout_mode = 2 70 | text = "Release Notes" 71 | 72 | [connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"] 73 | [connection signal="pressed" from="VBoxContainer/CenterContainer/DownloadButton" to="." method="_on_download_button_pressed"] 74 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/import_dialog/import_dialog.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | signal import_started(import_count: int) 5 | signal import_ended(data: Array[PandoraEntity]) 6 | 7 | @onready var notification_dialog = %NotificationDialog 8 | @onready var confirmation_dialog = %ConfirmationDialog 9 | @onready var file_dialog = %FileDialog 10 | 11 | 12 | func _ready(): 13 | file_dialog.file_selected.connect(_import_file) 14 | Pandora.import_success.connect(self._on_import_success) 15 | Pandora.import_failed.connect(self._on_import_failed) 16 | Pandora.import_calculation_ended.connect(self._on_import_calculation_ended) 17 | Pandora.import_calculation_failed.connect(self._on_import_calculation_failed) 18 | 19 | 20 | func open(): 21 | file_dialog.popup_centered() 22 | 23 | 24 | func _import_file(path: String) -> void: 25 | Pandora.calculate_import_data(path) 26 | 27 | 28 | func _on_import_calculation_failed(reason: String) -> void: 29 | notification_dialog.title = "Import Failed!" 30 | notification_dialog.dialog_text = reason 31 | notification_dialog.popup_centered() 32 | 33 | 34 | func _on_import_calculation_ended(import_info: Dictionary) -> void: 35 | confirmation_dialog.title = "Confirm Import" 36 | confirmation_dialog.dialog_text = ( 37 | "Found " 38 | + str(import_info["total_categories"]) 39 | + " Categories with " 40 | + str(import_info["total_entities"]) 41 | + " Entities. Would you like to proceed?" 42 | ) 43 | confirmation_dialog.confirmed.connect(func(): self._start_import(import_info)) 44 | confirmation_dialog.popup_centered() 45 | 46 | 47 | func _start_import(import_info: Dictionary) -> void: 48 | import_started.emit( 49 | ( 50 | int(import_info["total_categories"]) 51 | + int(import_info["total_entities"]) 52 | + int(import_info["total_properties"]) 53 | ) 54 | ) 55 | Pandora.import_data(import_info["path"]) 56 | 57 | 58 | func _on_import_success(imported_count: int = 0) -> void: 59 | var data: Array[PandoraEntity] = [] 60 | data.assign(Pandora.get_all_roots()) 61 | notification_dialog.title = "Import Finished!" 62 | notification_dialog.dialog_text = str(imported_count) + " records imported successfully!" 63 | notification_dialog.popup_centered() 64 | import_ended.emit(data) 65 | 66 | 67 | func _on_import_failed(reason: String) -> void: 68 | notification_dialog.title = "Import Failed!" 69 | notification_dialog.dialog_text = reason 70 | notification_dialog.popup_centered() 71 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/import_dialog/import_dialog.gd.uid: -------------------------------------------------------------------------------- 1 | uid://4hj8geca1kqp 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/import_dialog/import_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ceboo5esbe2ld"] 2 | 3 | [ext_resource type="Script" uid="uid://4hj8geca1kqp" path="res://addons/pandora/ui/editor/import_dialog/import_dialog.gd" id="1_4evpu"] 4 | 5 | [node name="ImportDialog" type="Control"] 6 | layout_mode = 3 7 | anchors_preset = 0 8 | offset_right = 40.0 9 | offset_bottom = 40.0 10 | script = ExtResource("1_4evpu") 11 | 12 | [node name="FileDialog" type="FileDialog" parent="."] 13 | unique_name_in_owner = true 14 | title = "Open a File" 15 | initial_position = 1 16 | size = Vector2i(800, 600) 17 | transient = false 18 | ok_button_text = "Open" 19 | file_mode = 0 20 | access = 2 21 | filters = PackedStringArray("*.pandora") 22 | 23 | [node name="NotificationDialog" type="AcceptDialog" parent="."] 24 | unique_name_in_owner = true 25 | title = "Import Finished!" 26 | initial_position = 1 27 | size = Vector2i(274, 100) 28 | transient = false 29 | dialog_text = "0 records imported successfully!" 30 | 31 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] 32 | unique_name_in_owner = true 33 | title = "Confirm Import" 34 | initial_position = 1 35 | size = Vector2i(504, 100) 36 | dialog_text = "Found 10 Categories with 4 Entities. Would you like to proceed?" 37 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_category_browser_property.gd: -------------------------------------------------------------------------------- 1 | extends EditorProperty 2 | 3 | # The main control for editing the property. 4 | var property_control := OptionButton.new() 5 | var ids_to_categories = {} 6 | 7 | 8 | func _init(class_data: Dictionary) -> void: 9 | # Add the control as a direct child of EditorProperty node. 10 | add_child.call_deferred(property_control) 11 | # Make sure the control is able to retain the focus. 12 | add_focusable(property_control) 13 | property_control.get_popup().id_pressed.connect(_on_id_selected) 14 | 15 | var id_counter = 0 16 | var all_categories = _find_all_categories(class_data["path"]) 17 | var editor_plugin: EditorPlugin = Engine.get_meta("PandoraEditorPlugin", null) 18 | # Prevent button from expanding to selected icon size. 19 | property_control.set_expand_icon(true) 20 | 21 | for category in all_categories: 22 | property_control.get_popup().add_icon_item( 23 | load(category.get_icon_path()), category.get_entity_name(), id_counter 24 | ) 25 | if editor_plugin: 26 | property_control.get_popup().set_item_icon_max_width(id_counter, editor_plugin.get_editor_interface().get_editor_scale() * 16) 27 | # Godot 4.1+ 28 | if property_control.get_popup().has_method("set_item_icon_modulate"): 29 | property_control.get_popup().set_item_icon_modulate(id_counter, category.get_icon_color()) 30 | ids_to_categories[id_counter] = category 31 | id_counter += 1 32 | 33 | 34 | func _on_id_selected(id: int) -> void: 35 | var category = ids_to_categories[id] as PandoraEntity 36 | var current_category = get_edited_object()[get_edited_property()] as PandoraEntity 37 | 38 | property_control.modulate = ( 39 | current_category.get_icon_color() if current_category != null else Color.WHITE 40 | ) 41 | if current_category != null and category.get_entity_id() == current_category.get_entity_id(): 42 | # skip current entities 43 | return 44 | 45 | emit_changed(get_edited_property(), category) 46 | 47 | 48 | func _update_property() -> void: 49 | _update_deferred() 50 | 51 | 52 | func _update_deferred() -> void: 53 | var current_category = get_edited_object()[get_edited_property()] as PandoraEntity 54 | if current_category == null: 55 | property_control.select(-1) 56 | return 57 | for id in ids_to_categories.keys(): 58 | if ids_to_categories[id].get_entity_id() == current_category.get_entity_id(): 59 | property_control.select(id) 60 | property_control.modulate = current_category.get_icon_color() 61 | break 62 | 63 | 64 | ## Looks up all categories who are eligible for the given script path 65 | func _find_all_categories(script_path: String) -> Array[PandoraEntity]: 66 | # lookup entity data 67 | var categories = Pandora.get_all_categories() 68 | var all_categories: Array[PandoraEntity] = [] 69 | for category in categories: 70 | if category._script_path == script_path: 71 | all_categories.append(category) 72 | if all_categories.is_empty(): 73 | all_categories = Pandora.get_all_categories() 74 | return all_categories 75 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_category_browser_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://pfvrw0lbudp5 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_instance_browser_property.gd: -------------------------------------------------------------------------------- 1 | extends EditorProperty 2 | 3 | # The main control for editing the property. 4 | var property_control := OptionButton.new() 5 | var ids_to_entities = {} 6 | 7 | 8 | func _init(class_data: Dictionary) -> void: 9 | # Add the control as a direct child of EditorProperty node. 10 | add_child.call_deferred(property_control) 11 | # Make sure the control is able to retain the focus. 12 | add_focusable(property_control) 13 | property_control.get_popup().id_pressed.connect(_on_id_selected) 14 | 15 | var id_counter = 0 16 | var all_entities = _find_all_entities(class_data["path"]) 17 | var editor_plugin: EditorPlugin = Engine.get_meta("PandoraEditorPlugin", null) 18 | # Prevent button from expanding to selected icon size. 19 | property_control.set_expand_icon(true) 20 | 21 | for entity in all_entities: 22 | property_control.get_popup().add_icon_item( 23 | load(entity.get_icon_path()), entity.get_entity_name(), id_counter 24 | ) 25 | if editor_plugin: 26 | property_control.get_popup().set_item_icon_max_width(id_counter, editor_plugin.get_editor_interface().get_editor_scale() * 16) 27 | # Godot 4.1+ 28 | if property_control.get_popup().has_method("set_item_icon_modulate"): 29 | property_control.get_popup().set_item_icon_modulate(id_counter, entity.get_icon_color()) 30 | ids_to_entities[id_counter] = entity 31 | id_counter += 1 32 | 33 | 34 | func _on_id_selected(id: int) -> void: 35 | var entity = ids_to_entities[id] as PandoraEntity 36 | var current_entity = get_edited_object()[get_edited_property()] as PandoraEntity 37 | 38 | property_control.modulate = ( 39 | current_entity.get_icon_color() if current_entity != null else Color.WHITE 40 | ) 41 | if current_entity != null and entity.get_entity_id() == current_entity.get_entity_id(): 42 | # skip current entities 43 | return 44 | 45 | emit_changed(get_edited_property(), entity) 46 | 47 | 48 | func _update_property() -> void: 49 | _update_deferred.call_deferred() 50 | 51 | 52 | func _update_deferred() -> void: 53 | var current_entity = get_edited_object()[get_edited_property()] as PandoraEntity 54 | if current_entity == null: 55 | property_control.select(-1) 56 | return 57 | for id in ids_to_entities.keys(): 58 | if ids_to_entities[id].get_entity_id() == current_entity.get_entity_id(): 59 | property_control.select(id) 60 | property_control.modulate = current_entity.get_icon_color() 61 | break 62 | 63 | 64 | ## Looks up all entities who are eligible for the given script path 65 | func _find_all_entities(script_path: String) -> Array[PandoraEntity]: 66 | # lookup entity data 67 | var categories = Pandora.get_all_categories() 68 | var all_entities: Array[PandoraEntity] = [] 69 | for category in categories: 70 | if category._script_path == script_path: 71 | var entities = Pandora.get_all_entities(category) 72 | for entity in entities: 73 | if entity in all_entities: continue 74 | all_entities.append(entity) 75 | if all_entities.is_empty(): 76 | all_entities = Pandora.get_all_entities() 77 | return all_entities 78 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_instance_browser_property.gd.uid: -------------------------------------------------------------------------------- 1 | uid://52wm80hygfi8 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_instance_inspector.gd: -------------------------------------------------------------------------------- 1 | extends EditorInspectorPlugin 2 | 3 | const EntityBrowserProperty = preload("./entity_instance_browser_property.gd") 4 | const CategoryBrowserProperty = preload("./entity_category_browser_property.gd") 5 | 6 | const PANDORA_ENTITY_CLASS = &"PandoraEntity" 7 | const PANDORA_CATEGORY_CLASS = &"PandoraCategory" 8 | 9 | # ClassName -> Dictionary 10 | var _global_class_cache = {} 11 | 12 | 13 | func _can_handle(object): 14 | return object != null 15 | 16 | 17 | func _parse_property(object, type, name, hint_type, hint_string, usage_flags, wide): 18 | if _global_class_cache.is_empty(): 19 | for global_class in ProjectSettings.get_global_class_list(): 20 | _global_class_cache[global_class["class"]] = global_class 21 | if type == TYPE_OBJECT: 22 | if _is_pandora_category(hint_string): 23 | var inspector_property := CategoryBrowserProperty.new(_global_class_cache[hint_string]) 24 | add_property_editor(name, inspector_property) 25 | return true 26 | elif _is_pandora_entity(hint_string): 27 | var inspector_property := EntityBrowserProperty.new(_global_class_cache[hint_string]) 28 | add_property_editor(name, inspector_property) 29 | return true 30 | return false 31 | return false 32 | 33 | 34 | func _is_pandora_entity(clazz: String) -> bool: 35 | if clazz == PANDORA_ENTITY_CLASS: 36 | return true 37 | if clazz == "": 38 | return false 39 | var parent = _get_parent_class(clazz) 40 | if parent == null: 41 | return false 42 | if parent == PANDORA_ENTITY_CLASS: 43 | return true 44 | return _is_pandora_entity(parent) 45 | 46 | 47 | func _is_pandora_category(clazz: String) -> bool: 48 | if clazz == PANDORA_CATEGORY_CLASS: 49 | return true 50 | if clazz == "": 51 | return false 52 | var parent = _get_parent_class(clazz) 53 | if parent == null: 54 | return false 55 | if parent == PANDORA_CATEGORY_CLASS: 56 | return true 57 | return _is_pandora_category(parent) 58 | 59 | 60 | func _get_parent_class(clazz_name: String) -> String: 61 | if not _global_class_cache.has(clazz_name): 62 | return "" 63 | var clazz = _global_class_cache[clazz_name] 64 | if not _global_class_cache.has(clazz["base"]): 65 | return "" 66 | return _global_class_cache[clazz["base"]]["class"] 67 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/inspector/entity_instance_inspector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://wifxwa5oif5s 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/pandora_editor.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b4f7fhcompuon 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_editor/property_editor.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HSplitContainer 3 | 4 | ## called when the user intends to navigate to the 5 | ## original property of an inherited property. 6 | signal inherited_property_selected(category_id: String, property_name: String) 7 | 8 | const PropertyControlKvp = preload( 9 | "res://addons/pandora/ui/components/properties/property_control_kvp.tscn" 10 | ) 11 | const PROPERTY_DEFAULT_NAME = "property" 12 | 13 | @onready var property_bar: PandoraPropertyBar = %PropertyBar 14 | @onready var property_list = %PropertyList 15 | @onready var unselected_container = %UnselectedContainer 16 | @onready var entity_attributes = %EntityAttributes 17 | @onready var property_settings_container = %PropertySettingsContainer 18 | @onready var scroll_container: ScrollContainer = %ScrollContainer 19 | 20 | var current_entity: PandoraEntity 21 | 22 | 23 | func _ready() -> void: 24 | property_bar.property_added.connect(_add_property) 25 | set_entity(null) 26 | 27 | 28 | ## if possible, attempt to edit the given property key. 29 | func edit_key(property_name: String) -> void: 30 | for property_control in property_list.get_children(): 31 | var property = property_control._property 32 | if ( 33 | property != null 34 | and property_name == property.get_property_name() 35 | and property.is_original() 36 | ): 37 | property_control.edit_key() 38 | 39 | 40 | func set_entity(entity: PandoraEntity) -> void: 41 | property_settings_container.visible = entity is PandoraCategory 42 | property_settings_container.set_property(null) 43 | for child in property_list.get_children(): 44 | child.queue_free() 45 | self.current_entity = entity 46 | property_bar.visible = entity != null and entity is PandoraCategory 47 | property_list.visible = entity != null 48 | unselected_container.visible = entity == null 49 | entity_attributes.visible = entity != null 50 | 51 | if entity != null: 52 | entity_attributes.init(entity) 53 | var properties = entity.get_entity_properties() 54 | 55 | for property in properties: 56 | var scene = property_bar.get_scene_by_type(property.get_property_type().get_type_name()) 57 | var control = scene.instantiate() as PandoraPropertyControl 58 | _add_property_control(control, property) 59 | 60 | 61 | func _add_property(scene: PackedScene) -> void: 62 | if not current_entity is PandoraCategory: 63 | print("Cannot add custom properties to non-categories!") 64 | return 65 | var control = scene.instantiate() as PandoraPropertyControl 66 | var property = Pandora.create_property( 67 | current_entity as PandoraCategory, 68 | _generate_property_name(control.type, current_entity), 69 | control.type 70 | ) 71 | if property != null: 72 | _add_property_control(control, property) 73 | 74 | 75 | func _add_property_control(control: PandoraPropertyControl, property: PandoraProperty) -> void: 76 | var control_kvp = PropertyControlKvp.instantiate() 77 | control.init(property) 78 | control_kvp.init(property, control, Pandora._entity_backend) 79 | control_kvp.inherited_property_selected.connect( 80 | func(category_id: String, property_name: String): inherited_property_selected.emit( 81 | category_id, property_name 82 | ) 83 | ) 84 | property_list.add_child(control_kvp) 85 | control_kvp.property_key_edit.grab_focus() 86 | control_kvp.original_property_selected.connect(property_settings_container.set_property) 87 | await get_tree().process_frame 88 | # First scroll to the control node and then apply an offset. 89 | # It's important to wait a frame before scrolling as for the documentation. 90 | scroll_container.ensure_control_visible(control_kvp) 91 | scroll_container.scroll_vertical = scroll_container.scroll_vertical + 100 92 | 93 | 94 | func _generate_property_name(type: String, entity: PandoraEntity) -> String: 95 | var properties = entity.get_entity_properties() 96 | var property_name = type + " " + PROPERTY_DEFAULT_NAME 97 | if properties.is_empty() or not entity.has_entity_property(property_name): 98 | return property_name 99 | return property_name + str(properties.size()) 100 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_editor/property_editor.gd.uid: -------------------------------------------------------------------------------- 1 | uid://df4b8473mfmny 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_editor/property_editor.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://clsxp8wg4ctet"] 2 | 3 | [ext_resource type="Script" uid="uid://df4b8473mfmny" path="res://addons/pandora/ui/editor/property_editor/property_editor.gd" id="1_r06w1"] 4 | [ext_resource type="PackedScene" uid="uid://8namx0kxhw0" path="res://addons/pandora/ui/components/property_bar/property_bar.tscn" id="1_vndtp"] 5 | [ext_resource type="PackedScene" uid="uid://ceqq28yvnhs2e" path="res://addons/pandora/ui/components/entity_attributes/entity_attributes.tscn" id="3_tosw7"] 6 | [ext_resource type="Texture2D" uid="uid://crgjwaubao8pj" path="res://addons/pandora/icons/icon.png" id="4_y2k2p"] 7 | [ext_resource type="PackedScene" uid="uid://c34sacps74dk3" path="res://addons/pandora/ui/editor/property_settings_editor/property_settings_editor.tscn" id="5_u6s37"] 8 | 9 | [node name="PropertyEditor" type="HSplitContainer"] 10 | offset_right = 519.0 11 | offset_bottom = 647.0 12 | size_flags_horizontal = 3 13 | size_flags_vertical = 3 14 | size_flags_stretch_ratio = 2.0 15 | script = ExtResource("1_r06w1") 16 | 17 | [node name="PropertyContainer" type="VBoxContainer" parent="."] 18 | layout_mode = 2 19 | size_flags_horizontal = 3 20 | size_flags_vertical = 3 21 | 22 | [node name="CaptionLabel" type="Label" parent="PropertyContainer"] 23 | layout_mode = 2 24 | theme_type_variation = &"HeaderSmall" 25 | text = "Properties" 26 | 27 | [node name="PanelContainer" type="PanelContainer" parent="PropertyContainer"] 28 | layout_mode = 2 29 | 30 | [node name="MarginContainer" type="MarginContainer" parent="PropertyContainer/PanelContainer"] 31 | layout_mode = 2 32 | theme_override_constants/margin_left = 5 33 | theme_override_constants/margin_top = 5 34 | theme_override_constants/margin_right = 5 35 | theme_override_constants/margin_bottom = 5 36 | 37 | [node name="HBoxContainer" type="HBoxContainer" parent="PropertyContainer/PanelContainer/MarginContainer"] 38 | layout_mode = 2 39 | 40 | [node name="PropertyBar" parent="PropertyContainer/PanelContainer/MarginContainer/HBoxContainer" instance=ExtResource("1_vndtp")] 41 | unique_name_in_owner = true 42 | visible = false 43 | layout_mode = 2 44 | 45 | [node name="ScrollContainer" type="ScrollContainer" parent="PropertyContainer"] 46 | unique_name_in_owner = true 47 | layout_mode = 2 48 | size_flags_vertical = 3 49 | follow_focus = true 50 | 51 | [node name="VBoxContainer" type="VBoxContainer" parent="PropertyContainer/ScrollContainer"] 52 | layout_mode = 2 53 | size_flags_horizontal = 3 54 | size_flags_vertical = 3 55 | 56 | [node name="PropertyList" type="VBoxContainer" parent="PropertyContainer/ScrollContainer/VBoxContainer"] 57 | unique_name_in_owner = true 58 | visible = false 59 | layout_mode = 2 60 | size_flags_horizontal = 3 61 | size_flags_vertical = 0 62 | 63 | [node name="UnselectedContainer" type="CenterContainer" parent="PropertyContainer/ScrollContainer/VBoxContainer"] 64 | unique_name_in_owner = true 65 | layout_mode = 2 66 | size_flags_horizontal = 3 67 | size_flags_vertical = 3 68 | 69 | [node name="VBoxContainer" type="VBoxContainer" parent="PropertyContainer/ScrollContainer/VBoxContainer/UnselectedContainer"] 70 | layout_mode = 2 71 | theme_override_constants/separation = 0 72 | alignment = 1 73 | 74 | [node name="TextureRect" type="TextureRect" parent="PropertyContainer/ScrollContainer/VBoxContainer/UnselectedContainer/VBoxContainer"] 75 | custom_minimum_size = Vector2(250, 250) 76 | layout_mode = 2 77 | size_flags_horizontal = 4 78 | size_flags_vertical = 0 79 | texture = ExtResource("4_y2k2p") 80 | expand_mode = 2 81 | stretch_mode = 5 82 | 83 | [node name="SpecialLabel" type="Label" parent="PropertyContainer/ScrollContainer/VBoxContainer/UnselectedContainer/VBoxContainer"] 84 | layout_mode = 2 85 | text = "Create or select a category or item to see its properties here." 86 | horizontal_alignment = 1 87 | vertical_alignment = 1 88 | 89 | [node name="EntityAttributes" parent="PropertyContainer/ScrollContainer/VBoxContainer" instance=ExtResource("3_tosw7")] 90 | unique_name_in_owner = true 91 | visible = false 92 | layout_mode = 2 93 | theme_override_constants/separation = 10 94 | 95 | [node name="PropertySettingsContainer" parent="." instance=ExtResource("5_u6s37")] 96 | unique_name_in_owner = true 97 | visible = false 98 | layout_mode = 2 99 | size_flags_stretch_ratio = 0.5 100 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_settings_editor/property_settings_editor.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends VBoxContainer 3 | 4 | const EntityPicker = preload("res://addons/pandora/ui/components/entity_picker/entity_picker.tscn") 5 | 6 | @onready var info_label = $InfoLabel 7 | @onready var header_label = $HeaderLabel 8 | @onready var properties_settings = $PropertiesSettings 9 | 10 | var _property: PandoraProperty 11 | var _default_settings: Dictionary 12 | var property_types_idx: Dictionary 13 | 14 | 15 | func set_property(property: PandoraProperty) -> void: 16 | for child in properties_settings.get_children(): 17 | child.queue_free() 18 | properties_settings.get_children().clear() 19 | self._property = property 20 | if property: 21 | var property_type = property.get_property_type() 22 | if property_type.get_type_name() == "array": 23 | self._default_settings = property_type.get_merged_settings(property) 24 | else: 25 | self._default_settings = property_type.get_settings() 26 | else: 27 | self._default_settings = {} 28 | info_label.visible = property == null or not property.is_original() 29 | header_label.visible = not info_label.visible 30 | 31 | for default_setting_name in _default_settings: 32 | var setting = HBoxContainer.new() 33 | setting.size_flags_horizontal = Control.SIZE_EXPAND_FILL 34 | var label = Label.new() 35 | label.size_flags_horizontal = Control.SIZE_EXPAND_FILL 36 | label.text = default_setting_name 37 | setting.add_child(label) 38 | var default_setting = _default_settings[default_setting_name] 39 | var current_value = ( 40 | _property.get_setting_override(default_setting_name) 41 | if _property.has_setting_override(default_setting_name) 42 | else default_setting.value 43 | ) 44 | var options: Array[Variant] = [] 45 | options.assign(default_setting["options"] if default_setting.has("options") else []) 46 | var control = _new_control_for_type( 47 | default_setting_name, 48 | default_setting.type, 49 | options, 50 | default_setting.value, 51 | current_value 52 | ) 53 | if control != null: 54 | control.size_flags_horizontal = Control.SIZE_EXPAND_FILL 55 | setting.add_child(control) 56 | properties_settings.add_child(setting) 57 | else: 58 | setting.queue_free() 59 | push_warning("Unsupported property setting type for " + str(default_setting_name)) 60 | 61 | 62 | func _new_control_for_type( 63 | key: String, 64 | type: String, 65 | options: Array[Variant], 66 | default_value: Variant, 67 | current_value: Variant 68 | ) -> Control: 69 | if options.size() > 0: 70 | var option_button = OptionButton.new() 71 | var popup = option_button.get_popup() 72 | var options_to_index = {} 73 | var index = 0 74 | for option in options: 75 | popup.add_radio_check_item(str(option), index) 76 | options_to_index[option] = index 77 | index = index + 1 78 | option_button.select(options_to_index[current_value]) 79 | popup.index_pressed.connect(func(index): _change_value(key, options[index], default_value)) 80 | return option_button 81 | elif type == "string": 82 | var edit = LineEdit.new() 83 | edit.text = current_value as String 84 | edit.text_changed.connect(func(new): _change_value(key, new, default_value)) 85 | return edit 86 | elif type == "color": 87 | var color_picker = ColorPickerButton.new() 88 | color_picker.color = current_value as Color 89 | color_picker.color_changed.connect(func(new): _change_value(key, new, default_value)) 90 | return color_picker 91 | elif type == "int" or type == "float": 92 | var spin_box = SpinBox.new() 93 | spin_box.min_value = -999999999 94 | spin_box.max_value = 999999999 95 | spin_box.step = 0.01 if type == "float" else 1 96 | spin_box.custom_arrow_step = spin_box.step 97 | spin_box.rounded = type == "int" 98 | spin_box.value = current_value 99 | spin_box.value_changed.connect(func(new): _change_value(key, new, default_value)) 100 | return spin_box 101 | elif type == "bool": 102 | var check_button = CheckButton.new() 103 | check_button.set_pressed(current_value as bool) 104 | check_button.toggled.connect(func(new): _change_value(key, new, default_value)) 105 | return check_button 106 | elif type == "reference": 107 | var entity_picker = EntityPicker.instantiate() 108 | entity_picker.categories_only = true 109 | # pre-select category deferred since 110 | # data may not be available right now 111 | if current_value != "": 112 | _select_category_on_picker.call_deferred(entity_picker, current_value) 113 | entity_picker.entity_selected.connect( 114 | func(new): _change_value(key, new.get_entity_id(), default_value) 115 | ) 116 | return entity_picker 117 | # pre-select category deferred since 118 | # data may not be available right now 119 | if current_value != "": 120 | _select_category_on_picker.call_deferred(entity_picker, current_value) 121 | entity_picker.entity_selected.connect( 122 | func(new): _change_value(key, new.get_entity_id(), default_value) 123 | ) 124 | return entity_picker 125 | elif type == "property_type": 126 | var property_type_picker: OptionButton = OptionButton.new() 127 | var idx = 0 128 | for property_type in PandoraPropertyType.get_all_types(): 129 | if property_type.allow_nesting(): 130 | property_type_picker.add_icon_item( 131 | load(property_type.get_type_icon_path()), property_type.get_type_name(), idx 132 | ) 133 | property_types_idx[idx] = property_type.get_type_name() 134 | idx += 1 135 | if current_value != "": 136 | _select_prop_type.call_deferred(property_type_picker, current_value) 137 | property_type_picker.item_selected.connect( 138 | func(idx): _change_value(key, property_types_idx[idx], default_value) 139 | ) 140 | return property_type_picker 141 | return null 142 | 143 | 144 | func _change_value(key: String, new_value: Variant, default_value: Variant) -> void: 145 | if new_value == default_value and _property.has_setting_override(key): 146 | _property.clear_setting_override(key) 147 | elif new_value != default_value: 148 | _property.set_setting_override(key, new_value) 149 | _refresh() 150 | 151 | 152 | func _select_category_on_picker(picker, category_id: String) -> void: 153 | var category = Pandora.get_category(category_id) 154 | picker.select(category) 155 | 156 | 157 | func _select_prop_type(picker, prop_type: String) -> void: 158 | picker.select(property_types_idx.find_key(prop_type)) 159 | 160 | 161 | func _refresh(): 162 | set_property(_property) 163 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_settings_editor/property_settings_editor.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bp70tavbds3k8 2 | -------------------------------------------------------------------------------- /addons/pandora/ui/editor/property_settings_editor/property_settings_editor.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c34sacps74dk3"] 2 | 3 | [ext_resource type="Script" uid="uid://bp70tavbds3k8" path="res://addons/pandora/ui/editor/property_settings_editor/property_settings_editor.gd" id="1_drmqk"] 4 | 5 | [node name="PropertySettingsContainer" type="VBoxContainer"] 6 | size_flags_horizontal = 3 7 | size_flags_vertical = 3 8 | script = ExtResource("1_drmqk") 9 | 10 | [node name="InfoLabel" type="Label" parent="."] 11 | layout_mode = 2 12 | size_flags_vertical = 3 13 | text = "Select an editable property 14 | to see its configuration!" 15 | horizontal_alignment = 1 16 | vertical_alignment = 1 17 | 18 | [node name="HeaderLabel" type="Label" parent="."] 19 | layout_mode = 2 20 | theme_type_variation = &"HeaderSmall" 21 | text = "Property Settings" 22 | 23 | [node name="PropertiesSettings" type="VBoxContainer" parent="."] 24 | layout_mode = 2 25 | -------------------------------------------------------------------------------- /addons/pandora/util/category_id_file_generator.gd: -------------------------------------------------------------------------------- 1 | const Tokenizer = preload("tokenizer.gd") 2 | 3 | 4 | class CategoryTuple: 5 | var category_name: String 6 | var category_id: String 7 | 8 | func _init(category_name: String, category_id: String) -> void: 9 | self.category_name = category_name 10 | self.category_id = category_id 11 | 12 | 13 | ## Generates a .gd file that allows for easier access 14 | ## of categories and subcategories of the data 15 | static func regenerate_category_id_file(root_categories: Array[PandoraCategory]) -> void: 16 | var root_category_tuples: Array[CategoryTuple] = generate_parent_category_tuples(root_categories) 17 | var subcategory_tuples: Dictionary = generate_sub_category_tuples(root_categories) 18 | generate_category_id_file(root_category_tuples, subcategory_tuples) 19 | 20 | 21 | static func generate_parent_category_tuples(root_categories: Array[PandoraCategory]) -> Array[CategoryTuple]: 22 | var root_category_tuples: Array[CategoryTuple] = [] 23 | for category in root_categories: 24 | root_category_tuples.append(CategoryTuple.new(category.get_entity_name(), category.get_entity_id())) 25 | return root_category_tuples 26 | 27 | 28 | static func generate_sub_category_tuples(root_categories: Array[PandoraCategory]) -> Dictionary: 29 | var subcategory_tuples = {} 30 | for category in root_categories: 31 | _process_sub_category_tuples(category.get_entity_name(), category._children, subcategory_tuples) 32 | return subcategory_tuples 33 | 34 | 35 | static func _process_sub_category_tuples( 36 | parent_category: String, entities: Array[PandoraEntity], subcategory_tuples: Dictionary) -> void: 37 | var local_subcategories = [] 38 | 39 | for entity in entities: 40 | if entity is PandoraCategory: 41 | var entity_name = entity.get_entity_name() 42 | var entity_id = entity.get_entity_id() 43 | var category_tuple = CategoryTuple.new(entity_name, entity_id) 44 | var new_key = parent_category + "_" + entity_name 45 | 46 | _process_sub_category_tuples(new_key, entity._children, subcategory_tuples) 47 | 48 | # Add current category to the local list if it's a leaf or has children 49 | if not subcategory_tuples.has(new_key) or subcategory_tuples[new_key].size() == 0: 50 | subcategory_tuples.erase(new_key) # Remove if empty 51 | local_subcategories.append(category_tuple) 52 | 53 | if local_subcategories.size() > 0: 54 | subcategory_tuples[parent_category] = local_subcategories 55 | 56 | 57 | 58 | static func generate_category_id_file( 59 | root_category_tuples: Array[CategoryTuple], subcategory_tuples: Dictionary) -> void: 60 | var file_path = "res://pandora/categories.gd" 61 | if not DirAccess.dir_exists_absolute("res://pandora"): 62 | DirAccess.make_dir_absolute("res://pandora") 63 | 64 | var file_access = FileAccess.open(file_path, FileAccess.WRITE) 65 | file_access.store_line("# Do not modify! Auto-generated file.") 66 | file_access.store_line("class_name PandoraCategories\n\n") 67 | 68 | for category_tuple in root_category_tuples: 69 | var line = 'const %s = "%s"' % [Tokenizer.tokenize(category_tuple.category_name), category_tuple.category_id] 70 | file_access.store_line(line) 71 | 72 | file_access.store_line("\n") 73 | for parent_category in subcategory_tuples: 74 | if subcategory_tuples[parent_category].size() == 0: 75 | continue 76 | var line = "class %sCategories:" % parent_category.to_pascal_case() 77 | file_access.store_line(line) 78 | for category_tuple in subcategory_tuples[parent_category]: 79 | line = ' const %s = "%s"' % [Tokenizer.tokenize(category_tuple.category_name), category_tuple.category_id] 80 | file_access.store_line(line) 81 | file_access.store_line("\n") 82 | file_access.close() 83 | -------------------------------------------------------------------------------- /addons/pandora/util/category_id_file_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://wti0o7u6m7l7 2 | -------------------------------------------------------------------------------- /addons/pandora/util/compression.gd: -------------------------------------------------------------------------------- 1 | const BLOCK_SIZE = 4096 2 | const MAGIC = "GCPF" 3 | 4 | 5 | ## magic 6 | ## char[4] "GCPF" 7 | ## 8 | ## header 9 | ## uint32_t compression_mode (Compression::MODE_ZSTD by default) 10 | ## uint32_t block_size (4096 by default) 11 | ## uint32_t uncompressed_size 12 | ## 13 | ## block compressed sizes, number of blocks = (uncompressed_size / block_size) + 1 14 | ## uint32_t block_sizes[] 15 | ## 16 | ## followed by compressed block data, same as calling `compress` for each source `block_size` 17 | static func compress(text: String, compression_mode:FileAccess.CompressionMode = FileAccess.COMPRESSION_FASTLZ) -> PackedByteArray: 18 | var data = _encode_string(text) 19 | var uncompressed_size = data.size() 20 | 21 | var num_blocks = int(ceil(float(uncompressed_size) / BLOCK_SIZE)) 22 | 23 | var buffer = PackedByteArray() 24 | 25 | buffer.append_array(_encode_string(MAGIC)) 26 | 27 | buffer.append_array(_encode_uint32(compression_mode)) 28 | buffer.append_array(_encode_uint32(BLOCK_SIZE)) 29 | buffer.append_array(_encode_uint32(uncompressed_size)) 30 | 31 | var block_sizes = PackedByteArray() 32 | var compressed_blocks = [] 33 | 34 | for i in range(num_blocks): 35 | var start = i * BLOCK_SIZE 36 | var end = min((i + 1) * BLOCK_SIZE, uncompressed_size) 37 | var block_data = PackedByteArray() 38 | var block_index = start 39 | while block_index < end: 40 | block_data.append(data[block_index]) 41 | block_index += 1 42 | 43 | var compressed_block = block_data.compress(compression_mode) 44 | block_sizes.append_array(_encode_uint32(compressed_block.size())) 45 | compressed_blocks.append(compressed_block) 46 | 47 | buffer.append_array(block_sizes) 48 | 49 | for block in compressed_blocks: 50 | buffer.append_array(block) 51 | 52 | return buffer 53 | 54 | 55 | static func _encode_uint32(value: int) -> PackedByteArray: 56 | var arr = PackedByteArray() 57 | arr.append(value & 0xFF) 58 | arr.append((value >> 8) & 0xFF) 59 | arr.append((value >> 16) & 0xFF) 60 | arr.append((value >> 24) & 0xFF) 61 | return arr 62 | 63 | 64 | static func _encode_string(value: String) -> PackedByteArray: 65 | var arr = PackedByteArray() 66 | for char in value: 67 | arr.append_array(char.to_ascii_buffer()) 68 | return arr 69 | -------------------------------------------------------------------------------- /addons/pandora/util/compression.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bwxfd2vjspwsh 2 | -------------------------------------------------------------------------------- /addons/pandora/util/entity_id_file_generator.gd: -------------------------------------------------------------------------------- 1 | const Tokenizer = preload("tokenizer.gd") 2 | 3 | static func regenerate_id_files(root_categories: Array[PandoraCategory]) -> void: 4 | var class_to_entity_map = generate_class_to_entity_map(root_categories) 5 | for entity_class in class_to_entity_map: 6 | var file_content = generate_entity_id_file(entity_class, class_to_entity_map[entity_class]) 7 | if not file_content.is_empty(): 8 | _write_to_file(entity_class, file_content) 9 | 10 | static func generate_class_to_entity_map(root_categories: Array[PandoraCategory]) -> Dictionary: 11 | var class_to_entity_map = {} 12 | for category in root_categories: 13 | _process_category_for_id_files(category, class_to_entity_map) 14 | 15 | # Remove empty entries from the map 16 | var keys_to_remove = [] 17 | for key in class_to_entity_map.keys(): 18 | if class_to_entity_map[key].size() == 0: 19 | keys_to_remove.append(key) 20 | for key in keys_to_remove: 21 | class_to_entity_map.erase(key) 22 | 23 | return class_to_entity_map 24 | 25 | static func generate_entity_id_file(entity_class_name: String, entities: Array[PandoraEntity]) -> Array[String]: 26 | if entities.is_empty(): 27 | return [] 28 | var lines:Array[String] = ["# Do not modify! Auto-generated file.", "class_name " + entity_class_name + "\n\n"] 29 | var name_usages = {} 30 | for entity in entities: 31 | var entity_name = entity.get_entity_name() 32 | if not name_usages.has(entity_name): 33 | name_usages[entity_name] = 0 34 | else: 35 | name_usages[entity_name] += 1 36 | entity_name += str(name_usages[entity_name]) 37 | lines.append("const " + Tokenizer.tokenize(entity_name) + ' = "' + entity.get_entity_id() + '"') 38 | return lines 39 | 40 | static func _process_category_for_id_files(category: PandoraCategory, class_to_entity_map: Dictionary) -> void: 41 | var classname = category.get_id_generation_class() 42 | if not class_to_entity_map.has(classname): 43 | var empty:Array[PandoraEntity] = [] 44 | class_to_entity_map[classname] = empty 45 | 46 | if category.is_generate_ids(): 47 | for child in category._children: 48 | if not child is PandoraCategory: 49 | if not _entity_exists_in_map(class_to_entity_map[classname], child): 50 | class_to_entity_map[classname].append(child) 51 | else: 52 | _process_category_for_id_files(child as PandoraCategory, class_to_entity_map) 53 | 54 | for child in category._children: 55 | if child is PandoraCategory: 56 | var child_classname = child.get_id_generation_class() 57 | if class_to_entity_map.has(child_classname): 58 | for sub_entity in class_to_entity_map[child_classname]: 59 | if not _entity_exists_in_map(class_to_entity_map[classname], sub_entity): 60 | class_to_entity_map[classname].append(sub_entity) 61 | 62 | static func _entity_exists_in_map(entity_list: Array[PandoraEntity], entity: PandoraEntity) -> bool: 63 | for e in entity_list: 64 | if e.get_entity_id() == entity.get_entity_id(): 65 | return true 66 | return false 67 | 68 | static func _write_to_file(entity_class_name: String, lines: Array[String]) -> void: 69 | var file_path = "res://pandora/" + entity_class_name.to_snake_case() + ".gd" 70 | if not DirAccess.dir_exists_absolute("res://pandora"): 71 | DirAccess.make_dir_absolute("res://pandora") 72 | 73 | var file = FileAccess.open(file_path, FileAccess.WRITE) 74 | if FileAccess.get_open_error() == OK: 75 | for line in lines: 76 | file.store_line(line) 77 | file.close() 78 | else: 79 | print("Failed to open file for writing: " + file_path) 80 | -------------------------------------------------------------------------------- /addons/pandora/util/entity_id_file_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ra4xv74fia7e 2 | -------------------------------------------------------------------------------- /addons/pandora/util/id_generator.gd: -------------------------------------------------------------------------------- 1 | class_name PandoraIDGenerator 2 | extends RefCounted 3 | 4 | var _nanoid := PandoraNanoIDGenerator.new(10) 5 | var _sequential := PandoraSequentialIDGenerator.new() 6 | 7 | 8 | func generate() -> String: 9 | var id_type := PandoraSettings.get_id_type() 10 | match id_type: 11 | PandoraSettings.IDType.SEQUENTIAL: 12 | return _sequential.generate() 13 | PandoraSettings.IDType.NANOID: 14 | return _nanoid.generate() 15 | push_error("unknown id type: %s" % id_type) 16 | return _sequential.generate() 17 | 18 | 19 | func clear() -> void: 20 | _sequential.clear() 21 | 22 | 23 | func save_data() -> Dictionary: 24 | return _sequential.save_data() 25 | 26 | 27 | func load_data(data: Dictionary) -> void: 28 | _sequential.load_data(data) 29 | -------------------------------------------------------------------------------- /addons/pandora/util/id_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dr36cdy1l3e1d 2 | -------------------------------------------------------------------------------- /addons/pandora/util/nanoid_generator.gd: -------------------------------------------------------------------------------- 1 | # inspired by github.com/eth0net/nanoid-godot (MIT) v0.2.0 2 | class_name PandoraNanoIDGenerator 3 | extends RefCounted 4 | 5 | const ALPHABET := "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict" 6 | const DEFAULT_LENGTH := 21 7 | 8 | var default_length := DEFAULT_LENGTH 9 | 10 | 11 | func _init(length := DEFAULT_LENGTH) -> void: 12 | default_length = length 13 | 14 | 15 | func generate(length := default_length) -> String: 16 | var id: String 17 | for i in range(length): 18 | id += ALPHABET[randi() % ALPHABET.length()] 19 | return id 20 | -------------------------------------------------------------------------------- /addons/pandora/util/nanoid_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dnafvia3jdolr 2 | -------------------------------------------------------------------------------- /addons/pandora/util/script_util.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const PandoraEntityScript = preload("res://addons/pandora/model/entity.gd") 4 | 5 | 6 | static func create_entity_from_script( 7 | path: String, id: String, name: String, icon_path: String, category_id: String 8 | ): 9 | var clazz = _get_entity_class(path) 10 | var new_method = _find_first_method_of_script(clazz, "init_entity") 11 | if not new_method.has("args"): 12 | push_warning("ERROR - Pandora is unable to correctly resolve new() method.") 13 | var entity = PandoraEntityScript.new() 14 | entity.init_entity(id, name, icon_path, category_id) 15 | return entity 16 | var expected_method = _find_first_method_of_script(PandoraEntityScript, "init_entity") 17 | if new_method["args"].size() != expected_method["args"].size(): 18 | push_warning( 19 | ( 20 | "init_entity() method has incorrect signature! Requires " 21 | + str(expected_method["args"].size()) 22 | + " arguments - defaulting to PandoraEntity instead." 23 | ) 24 | ) 25 | var entity = PandoraEntityScript.new() 26 | entity.init_entity(id, name, icon_path, category_id) 27 | return entity 28 | 29 | var entity = clazz.new() 30 | if not entity is PandoraEntity: 31 | push_warning( 32 | "Script '" + path + "' must extend PandoraEntity - defaulting to PandoraEntity instead." 33 | ) 34 | entity = PandoraEntityScript.new() 35 | 36 | entity.init_entity(id, name, icon_path, category_id) 37 | return entity 38 | 39 | 40 | static func _get_entity_class(path: String) -> GDScript: 41 | var EntityClass = load(path) 42 | if EntityClass == null or not EntityClass.can_instantiate(): 43 | push_warning("Unable to find " + path + " - defaulting to PandoraEntity instead.") 44 | EntityClass = PandoraEntityScript 45 | return EntityClass 46 | 47 | 48 | ## searches for the first occurence of the method. 49 | ## methods can occur multiple times in the order of inheritance. 50 | static func _find_first_method_of_script(script: GDScript, method_name: String) -> Dictionary: 51 | for method in script.get_script_method_list(): 52 | if method.name == method_name: 53 | return method 54 | return {} 55 | -------------------------------------------------------------------------------- /addons/pandora/util/script_util.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cmv0h32r644wp 2 | -------------------------------------------------------------------------------- /addons/pandora/util/sequential_id_generator.gd: -------------------------------------------------------------------------------- 1 | class_name PandoraSequentialIDGenerator 2 | extends RefCounted 3 | 4 | const DEFAULT_CONTEXT: String = "default" 5 | 6 | var _ids_by_context: Dictionary = {} 7 | 8 | 9 | func generate(context: String = DEFAULT_CONTEXT) -> String: 10 | if not _ids_by_context.has(context): 11 | _ids_by_context[context] = 0 12 | _ids_by_context[context] += 1 13 | return str(_ids_by_context[context]) 14 | 15 | 16 | func clear() -> void: 17 | _ids_by_context.clear() 18 | 19 | 20 | func save_data() -> Dictionary: 21 | return {"_ids_by_context": _ids_by_context} 22 | 23 | 24 | func load_data(data: Dictionary) -> void: 25 | _ids_by_context = data["_ids_by_context"] 26 | -------------------------------------------------------------------------------- /addons/pandora/util/sequential_id_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgb1rend0hi7r 2 | -------------------------------------------------------------------------------- /addons/pandora/util/tokenizer.gd: -------------------------------------------------------------------------------- 1 | static var regex:RegEx 2 | 3 | ## Converts any string into a GDScript-valid token (cannot contain special characters) 4 | static func tokenize(value:String) -> String: 5 | if regex == null: 6 | regex = RegEx.new() 7 | regex.compile("(^[^A-Z_\u4e00-\u9fa5])|([^A-Z0-9_\u4e00-\u9fa5])") 8 | var token = value.to_upper() 9 | 10 | var regex_matches = regex.search_all(token) 11 | var offset = 0 12 | for regex_match in regex_matches: 13 | var start = regex_match.get_start() 14 | var end = regex_match.get_end() 15 | var length = end - start 16 | var text = token.substr(start + offset, length) 17 | var replacement = "_" 18 | token = token.substr(0, start + offset) + replacement + token.substr(end + offset) 19 | offset += replacement.length() - text.length() 20 | 21 | return token 22 | -------------------------------------------------------------------------------- /addons/pandora/util/tokenizer.gd.uid: -------------------------------------------------------------------------------- 1 | uid://diww4ex65mx8k 2 | --------------------------------------------------------------------------------