Add addons

This commit is contained in:
Joey Yakimowich-Payne 2023-01-03 16:53:53 -07:00
commit 3367bad24f
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
527 changed files with 36197 additions and 0 deletions

View file

@ -0,0 +1,511 @@
@tool
extends DialogicEditor
## Editor for editing character resources.
signal character_loaded(resource_path:String)
signal portrait_selected()
# Enums
enum PreviewModes {Full, Real}
# Current state
var current_preview_mode = PreviewModes.Full
var loading = false
var current_previewed_scene = null
# References
var selected_item: TreeItem
##############################################################################
## RESOURCE LOGIC
##############################################################################
# Method is called once editors manager is ready to accept registers.
func _register() -> void:
# Makes the editor open this when a .dch file is selected.
# Then _open_resource() is called.
editors_manager.register_resource_editor("dch", self)
# Add an "add character" button
var add_character_button = editors_manager.add_icon_button(
load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"),
'Add Character',
self)
add_character_button.pressed.connect(
editors_manager.show_add_resource_dialog.bind(
new_character,
'*.dch; DialogicCharacter',
'Create new character',
'character',
))
# Called when a character is opened somehow
func _open_resource(resource:Resource) -> void:
# update resource
current_resource = (resource as DialogicCharacter)
# make sure changes in the ui won't trigger saving
loading = true
## Load other main tabs
for main_edit in %MainEditTabs.get_children():
main_edit._load_character(current_resource)
%DefaultPortraitPicker.set_value(resource.default_portrait)
%MainScale.value = 100*resource.scale
%MainOffsetX.value = resource.offset.x
%MainOffsetY.value = resource.offset.y
%MainMirror.button_pressed = resource.mirror
# Portrait section
%PortraitSearch.text = ""
load_portrait_tree()
loading = false
character_loaded.emit(resource.resource_path)
func _save_resource() -> void:
if ! visible or not current_resource:
return
# Portrait list
current_resource.portraits = get_updated_portrait_dict()
# Portrait settings
if %DefaultPortraitPicker.current_value in current_resource.portraits.keys():
current_resource.default_portrait = %DefaultPortraitPicker.current_value
elif !current_resource.portraits.is_empty():
current_resource.default_portrait = current_resource.portraits.keys()[0]
else:
current_resource.default_portrait = ""
current_resource.scale = %MainScale.value/100.0
current_resource.offset = Vector2(%MainOffsetX.value, %MainOffsetY.value)
current_resource.mirror = %MainMirror.button_pressed
# Main tabs
for main_edit in %MainEditTabs.get_children():
current_resource = main_edit._save_changes(current_resource)
ResourceSaver.save(current_resource, current_resource.resource_path)
current_resource_state = ResourceStates.Saved
editors_manager.resource_helper.rebuild_character_directory()
# Saves a new empty character to the given path
func new_character(path: String) -> void:
var resource := DialogicCharacter.new()
resource.resource_path = path
resource.display_name = path.get_file().trim_suffix("."+path.get_extension())
resource.color = Color(1,1,1,1)
resource.default_portrait = ""
resource.custom_info = {}
ResourceSaver.save(resource, path)
editors_manager.edit_resource(resource)
##############################################################################
## INTERFACE
##############################################################################
func _ready() -> void:
setup_portrait_list_tab()
setup_portrait_settings_tab()
%PreviewMode.item_selected.connect(_on_PreviewMode_item_selected)
%PreviewMode.select(DialogicUtil.get_project_setting('dialogic/editor/character_preview_mode', 0))
_on_PreviewMode_item_selected(%PreviewMode.selected)
## General Styling
var panel_style = DCSS.inline({
'border-radius': 5,
'border': 1,
'border_color':get_theme_color("dark_color_3", "Editor"),
'background': get_theme_color("base_color", "Editor"),
'padding': [10, 10],
})
var tab_panel :StyleBoxFlat = get_theme_stylebox('tab_selected', 'TabContainer').duplicate()
tab_panel.bg_color = get_theme_color("base_color", "Editor")
%MainEditTabs.add_theme_stylebox_override('panel', panel_style)
%MainEditTabs.add_theme_stylebox_override('tab_selected', tab_panel)
%MainEditTabs.add_theme_constant_override('side_margin', 5)
%PortraitListSection.add_theme_stylebox_override('panel', panel_style)
%PortraitListSection.add_theme_stylebox_override('tab_selected', tab_panel)
%PortraitListSection.add_theme_constant_override('side_margin', 5)
%PortraitPreviewSection.add_theme_stylebox_override('panel', panel_style)
%PortraitSettingsSection.add_theme_stylebox_override('panel', panel_style)
%PortraitSettingsSection.add_theme_stylebox_override('tab_selected', tab_panel)
%PortraitSettingsSection.add_theme_constant_override('side_margin', 5)
%RealPreviewPivot.texture = get_theme_icon("EditorPivot", "EditorIcons")
# Add general tab
add_main_tab("res://addons/dialogic/Editor/CharacterEditor/character_editor_tab_general.tscn")
# Load main tabs from subsystems/events
for indexer in DialogicUtil.get_indexers():
for main_tab in indexer._get_character_editor_tabs():
add_main_tab(main_tab)
for child in %PortraitSettingsSection.get_children():
if !child is DialogicCharacterEditorPortraitSettingsTab:
printerr("[Dialogic Editor] Portrait settings tabs should extend the right class!")
else:
child.character_editor = self
child.changed.connect(something_changed)
child.update_preview.connect(update_preview)
func add_main_tab(scene_path:String) -> void:
var edit: DialogicCharacterEditorMainTab = load(scene_path).instantiate()
edit.changed.connect(something_changed)
edit.character_editor = self
%MainEditTabs.add_child(edit)
func something_changed(fake_argument = "", fake_arg2 = null) -> void:
if not loading:
current_resource_state = ResourceStates.Unsaved
editors_manager.save_current_resource() #TODO, should this happen?
##############################################################################
## PORTRAIT SECTION
##############################################################################
func setup_portrait_list_tab() -> void:
%PortraitTree.editor = self
## Portrait section styling/connections
%AddPortraitButton.icon = get_theme_icon("Add", "EditorIcons")
%AddPortraitButton.pressed.connect(add_portrait)
%AddPortraitGroupButton.icon = get_theme_icon("Groups", "EditorIcons")
%AddPortraitGroupButton.pressed.connect(add_portrait_group)
%ImportPortraitsButton.icon = get_theme_icon("Folder", "EditorIcons")
%ImportPortraitsButton.pressed.connect(open_portrait_folder_select)
%PortraitSearch.right_icon = get_theme_icon("Search", "EditorIcons")
%PortraitSearch.text_changed.connect(filter_portrait_list)
%PortraitTree.item_selected.connect(load_selected_portrait)
%PortraitTree.item_edited.connect(_on_item_edited)
func open_portrait_folder_select() -> void:
find_parent("EditorView").godot_file_dialog(
import_portraits_from_folder, "*",
EditorFileDialog.FILE_MODE_OPEN_DIR)
func import_portraits_from_folder(path:String) -> void:
var parent: TreeItem = %PortraitTree.get_root()
if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
parent = %PortraitTree.get_selected()
var dir := DirAccess.open(path)
dir.list_dir_begin()
var file_name :String = dir.get_next()
while file_name != "":
if not dir.current_is_dir():
var file_lower = file_name.to_lower()
if '.svg' in file_lower or '.png' in file_lower:
if not '.import' in file_lower:
var final_name: String= path+ "/" + file_name
%PortraitTree.add_portrait_item(file_name.trim_suffix('.'+file_name.get_extension()),
{'scene':"",'image':final_name, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent)
file_name = dir.get_next()
something_changed()
func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary={'scene':"", 'image':'', 'scale':1, 'offset':Vector2(), 'mirror':false}) -> void:
var parent: TreeItem = %PortraitTree.get_root()
if %PortraitTree.get_selected():
if %PortraitTree.get_selected().get_metadata(0).has('group'):
parent = %PortraitTree.get_selected()
else:
parent = %PortraitTree.get_selected().get_parent()
%PortraitTree.add_portrait_item(portrait_name, portrait_data, parent).select(0)
something_changed()
func add_portrait_group() -> void:
var parent_item :TreeItem = %PortraitTree.get_root()
if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
parent_item = %PortraitTree.get_selected()
%PortraitTree.add_portrait_group("Group", parent_item)
func load_portrait_tree() -> void:
%PortraitTree.clear_tree()
var root:TreeItem = %PortraitTree.create_item()
for portrait in current_resource.portraits.keys():
var portrait_label = portrait
var parent = %PortraitTree.get_root()
if '/' in portrait:
parent = %PortraitTree.create_necessary_group_items(portrait)
portrait_label = portrait.split('/')[-1]
%PortraitTree.add_portrait_item(portrait_label, current_resource.portraits[portrait], parent)
update_default_portrait_star(current_resource.default_portrait)
if root.get_child_count():
root.get_first_child().select(0)
else:
# Call anyways to clear preview and hide portrait settings section
load_selected_portrait()
func filter_portrait_list(filter_term:String = '') -> void:
filter_branch(%PortraitTree.get_root(), filter_term)
func filter_branch(parent:TreeItem, filter_term:String) -> bool:
var anything_visible := false
for item in parent.get_children():
if item.get_metadata(0).has('group'):
item.visible = filter_branch(item, filter_term)
anything_visible = item.visible
elif filter_term.is_empty() or filter_term.to_lower() in item.get_text(0).to_lower():
item.visible = true
anything_visible = true
else:
item.visible = false
return anything_visible
# this is used to save the portrait data
func get_updated_portrait_dict() -> Dictionary:
return list_portraits(%PortraitTree.get_root().get_children())
func list_portraits(tree_items:Array[TreeItem], dict:Dictionary = {}, path_prefix = "") -> Dictionary:
for item in tree_items:
if item.get_metadata(0).has('group'):
dict = list_portraits(item.get_children(), dict, path_prefix+item.get_text(0)+"/")
else:
dict[path_prefix +item.get_text(0)] = item.get_metadata(0)
return dict
func load_selected_portrait():
if selected_item and is_instance_valid(selected_item):
selected_item.set_editable(0, false)
selected_item = %PortraitTree.get_selected()
if selected_item and !selected_item.get_metadata(0).has('group'):
%PortraitSettingsSection.show()
var current_portrait_data :Dictionary = selected_item.get_metadata(0)
portrait_selected.emit(%PortraitTree.get_full_item_name(selected_item), current_portrait_data)
update_preview()
for tab in %PortraitSettingsSection.get_children():
if !tab is DialogicCharacterEditorPortraitSettingsTab:
printerr("[Dialogic Editor] Portrait settings tabs should extend the right class!")
else:
tab.selected_item = selected_item
tab._load_portrait_data(current_portrait_data)
# switch tabs if the current one is hidden (until the next not hidden tab)
for i in range(%PortraitSettingsSection.get_tab_count()):
if %PortraitSettingsSection.is_tab_hidden(%PortraitSettingsSection.current_tab):
if %PortraitSettingsSection.current_tab == %PortraitSettingsSection.get_tab_count()-1:
%PortraitSettingsSection.current_tab = 0
else:
%PortraitSettingsSection.current_tab += 1
else:
break
else:
%PortraitSettingsSection.hide()
update_preview()
if selected_item:
await get_tree().create_timer(0.01).timeout
selected_item.set_editable(0, true)
func delete_portrait_item(item:TreeItem) -> void:
item.free()
something_changed()
func duplicate_item(item:TreeItem) -> void:
%PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0), item.get_parent()).select(0)
func _on_portrait_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_index:int):
# DELETE BUTTON
if id == 1:
delete_portrait_item(item)
# DUPLICATE ITEM
if id == 3:
duplicate_item(item)
# this removes/and adds the DEFAULT star on the portrait list
func update_default_portrait_star(default_portrait_name:String) -> void:
var item_list : Array = %PortraitTree.get_root().get_children()
while true:
var item = item_list.pop_back()
if item.get_button_by_id(0, 2) != -1:
item.erase_button(0, item.get_button_by_id(0, 2))
if %PortraitTree.get_full_item_name(item) == default_portrait_name:
item.erase_button(0, item.get_button_by_id(0, 1))
item.erase_button(0, item.get_button_by_id(0, 3))
item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default')
item.add_button(0, get_theme_icon('Duplicate', 'EditorIcons'), 3, false, 'Duplicate')
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
item_list.append_array(item.get_children())
if item_list.is_empty():
break
func _on_item_edited():
selected_item = %PortraitTree.get_selected()
something_changed()
if selected_item:
if %PreviewLabel.text.trim_prefix('Preview of "').trim_suffix('"') == current_resource.default_portrait:
current_resource.default_portrait = %PortraitTree.get_full_item_name(selected_item)
update_preview()
##############################################################################
## PORTRAIT SETTINGS TAB
##############################################################################
func setup_portrait_settings_tab() -> void:
%DefaultPortraitPicker.value_changed.connect(default_portrait_changed)
%MainScale.value_changed.connect(main_portrait_settings_update)
%MainOffsetX.value_changed.connect(main_portrait_settings_update)
%MainOffsetY.value_changed.connect(main_portrait_settings_update)
%MainMirror.toggled.connect(main_portrait_settings_update)
# Setting up Default Portrait Picker
%DefaultPortraitPicker.resource_icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")
%DefaultPortraitPicker.get_suggestions_func = suggest_portraits
%DefaultPortraitPicker.set_left_text("")
# Make sure preview get's updated when portrait settings change
func main_portrait_settings_update(value = null) -> void:
current_resource.scale = %MainScale.value/100.0
current_resource.offset = Vector2(%MainOffsetX.value, %MainOffsetY.value)
current_resource.mirror = %MainMirror.button_pressed
update_preview()
something_changed()
func default_portrait_changed(property:String, value:String) -> void:
current_resource.default_portrait = value
update_default_portrait_star(value)
# Get suggestions for DefaultPortraitPicker
func suggest_portraits(search:String) -> Dictionary:
var suggestions := {}
for portrait in get_updated_portrait_dict().keys():
suggestions[portrait] = {'value':portrait}
return suggestions
##############################################################################
## PREVIEW
##############################################################################
func update_preview() -> void:
%ScenePreviewWarning.hide()
if selected_item and is_instance_valid(selected_item) and !selected_item.get_metadata(0).has('group'):
%PreviewLabel.text = 'Preview of "'+%PortraitTree.get_full_item_name(selected_item)+'"'
var current_portrait_data: Dictionary = selected_item.get_metadata(0)
var mirror:bool = current_portrait_data.get('mirror', false) != current_resource.mirror
var scale:float = current_portrait_data.get('scale', 1) * current_resource.scale
var offset:Vector2 =current_portrait_data.get('offset', Vector2()) + current_resource.offset
if current_previewed_scene != null \
and current_previewed_scene.get_meta('path', null) == current_portrait_data.get('scene') \
and current_previewed_scene.has_method('_should_do_portrait_update') \
and is_instance_valid(current_previewed_scene.get_script()) \
and current_previewed_scene._should_do_portrait_update(current_resource, selected_item.get_text(0)):
pass # we keep the same scene
else:
for node in %RealPreviewPivot.get_children():
node.queue_free()
current_previewed_scene = null
if current_portrait_data.get('scene', '').is_empty():
if FileAccess.file_exists("res://addons/dialogic/Events/Character/default_portrait.tscn"):
current_previewed_scene = load("res://addons/dialogic/Events/Character/default_portrait.tscn").instantiate()
current_previewed_scene.set_meta('path', '')
else:
if FileAccess.file_exists(current_portrait_data.get('scene')):
current_previewed_scene = load(current_portrait_data.get('scene')).instantiate()
current_previewed_scene.set_meta('path', current_portrait_data.get('scene'))
if current_previewed_scene:
%RealPreviewPivot.add_child(current_previewed_scene)
if current_previewed_scene != null:
var scene = current_previewed_scene
scene.show_behind_parent = true
for prop in current_portrait_data.get('export_overrides', {}).keys():
scene.set(prop, str_to_var(current_portrait_data['export_overrides'][prop]))
if is_instance_valid(scene.get_script()) and scene.script.is_tool():
if scene.has_method('_update_portrait'):
scene._update_portrait(current_resource, %PortraitTree.get_full_item_name(selected_item))
if scene.has_method('_set_mirror'):
scene._set_mirror(mirror)
if current_preview_mode == PreviewModes.Real:
scene.position = Vector2() + offset
if current_portrait_data.get('scene', '').is_empty() or !current_portrait_data.get('ignore_char_scale', false):
scene.scale = Vector2(1,1)*scale
else:
if is_instance_valid(scene.get_script()) and scene.script.is_tool() and scene.has_method('_get_covered_rect'):
var rect :Rect2= scene._get_covered_rect()
var available_rect:Rect2 = %FullPreviewAvailableRect.get_rect()
scene.scale = Vector2(1,1) * min(available_rect.size.x/rect.size.x, available_rect.size.y/rect.size.y)
%RealPreviewPivot.position = (rect.position)*-1*scene.scale
%RealPreviewPivot.position.x = %FullPreviewAvailableRect.size.x/2
scene.position = Vector2()
else:
%ScenePreviewWarning.show()
else:
%PreviewRealRect.texture = null
%PreviewFullRect.texture = null
%PreviewLabel.text = 'Nothing to preview'
else:
%PreviewLabel.text = 'No portrait to preview.'
for node in %RealPreviewPivot.get_children():
node.queue_free()
current_previewed_scene = null
func _on_PreviewMode_item_selected(index:int) -> void:
current_preview_mode = index
# FULL VIEW
if index == PreviewModes.Full:
%RealSizeRemotePivotTransform.update_position = false
# REAL SIZE
if index == PreviewModes.Real or index == null:
%RealSizeRemotePivotTransform.update_position = true
update_preview()
ProjectSettings.set_setting('dialogic/editor/character_preview_mode', index)
ProjectSettings.save()
func _on_full_preview_available_rect_resized():
if current_preview_mode == PreviewModes.Full:
update_preview()

View file

@ -0,0 +1,493 @@
[gd_scene load_steps=15 format=3 uid="uid://dlskc36c5hrwv"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="2_01va3"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_image.gd" id="2_cusvj"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd" id="2_vad0i"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_scene.gd" id="4_6tltp"]
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="4_a4bcu"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_layout.gd" id="5_eicvp"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_exports.gd" id="7_4enie"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kxihh"]
content_margin_left = 10.0
content_margin_top = 4.0
content_margin_right = 10.0
content_margin_bottom = 4.0
bg_color = Color(0, 0, 0, 1)
border_width_top = 2
border_color = Color(1, 1, 1, 0.75)
corner_detail = 1
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1pdx5"]
content_margin_left = 7.5
content_margin_top = 7.5
content_margin_right = 7.5
content_margin_bottom = 7.5
bg_color = Color(0, 0, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="Image" id="Image_8q5e2"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_uf4x7"]
image = SubResource("Image_8q5e2")
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4xgdx"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_81xk6"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(1, 0.365, 0.365, 1)
draw_center = false
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
corner_detail = 1
[node name="CharacterEditor" type="MarginContainer"]
self_modulate = Color(0, 0, 0, 1)
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 0
theme_override_constants/margin_top = 0
theme_override_constants/margin_right = 0
theme_override_constants/margin_bottom = 0
script = ExtResource("2")
[node name="Split" type="HSplitContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Editor" type="VSplitContainer" parent="Split"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 11
[node name="EditorScroll" type="ScrollContainer" parent="Split/Editor"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.0
vertical_scroll_mode = 0
[node name="MainEditTabs" type="TabContainer" parent="Split/Editor/EditorScroll"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/side_margin = 5
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
[node name="PortraitListSection" type="TabContainer" parent="Split/Editor"]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/side_margin = 5
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
[node name="Portraits" type="VBoxContainer" parent="Split/Editor/PortraitListSection"]
layout_mode = 2
[node name="PortraitListTools" type="HBoxContainer" parent="Split/Editor/PortraitListSection/Portraits"]
layout_mode = 2
[node name="AddPortraitButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Add portrait"
icon = SubResource("ImageTexture_uf4x7")
[node name="AddPortraitGroupButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Add Group"
icon = SubResource("ImageTexture_uf4x7")
[node name="ImportPortraitsButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Import images from folder"
icon = SubResource("ImageTexture_uf4x7")
[node name="PortraitSearch" type="LineEdit" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 4
placeholder_text = "Search"
expand_to_text_length = true
clear_button_enabled = true
right_icon = SubResource("ImageTexture_uf4x7")
caret_blink = true
caret_blink_interval = 0.5
[node name="Panel" type="PanelContainer" parent="Split/Editor/PortraitListSection/Portraits"]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxEmpty_4xgdx")
[node name="PortraitTree" type="Tree" parent="Split/Editor/PortraitListSection/Portraits/Panel"]
unique_name_in_owner = true
layout_mode = 2
allow_rmb_select = true
hide_root = true
drop_mode_flags = 1
script = ExtResource("2_vad0i")
[node name="P Settings" type="VBoxContainer" parent="Split/Editor/PortraitListSection"]
visible = false
layout_mode = 2
[node name="DefaultPortrait" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label5" type="Label" parent="Split/Editor/PortraitListSection/P Settings/DefaultPortrait"]
layout_mode = 2
text = "Default:"
[node name="DefaultPortraitPicker" parent="Split/Editor/PortraitListSection/P Settings/DefaultPortrait" instance=ExtResource("2_01va3")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Select Portrait"
[node name="HSeparator" type="HSeparator" parent="Split/Editor/PortraitListSection/P Settings"]
layout_mode = 2
[node name="PortraitMainSettings" type="HFlowContainer" parent="Split/Editor/PortraitListSection/P Settings"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
layout_mode = 2
text = "Scale"
[node name="MainScale" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
unique_name_in_owner = true
layout_mode = 2
value = 100.0
allow_greater = true
suffix = "%"
[node name="Offset" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
layout_mode = 2
[node name="Label2" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
layout_mode = 2
text = "Offset"
[node name="MainOffsetX" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
unique_name_in_owner = true
layout_mode = 2
allow_greater = true
allow_lesser = true
suffix = "X"
[node name="MainOffsetY" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
unique_name_in_owner = true
layout_mode = 2
allow_greater = true
allow_lesser = true
suffix = "Y"
[node name="MirrorOption" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
layout_mode = 2
[node name="Label" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/MirrorOption"]
layout_mode = 2
text = "Mirror"
[node name="MainMirror" type="CheckBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/MirrorOption"]
unique_name_in_owner = true
layout_mode = 2
[node name="RightSection" type="VSplitContainer" parent="Split"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
size_flags_stretch_ratio = 1.5
[node name="PortraitPreviewSection" type="Panel" parent="Split/RightSection"]
unique_name_in_owner = true
clip_contents = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
[node name="Node2D" type="Node2D" parent="Split/RightSection/PortraitPreviewSection"]
position = Vector2(13, 17)
[node name="RealPreviewPivot" type="Sprite2D" parent="Split/RightSection/PortraitPreviewSection/Node2D"]
unique_name_in_owner = true
position = Vector2(330, 405)
texture = SubResource("ImageTexture_uf4x7")
[node name="ScenePreviewWarning" type="Label" parent="Split/RightSection/PortraitPreviewSection"]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -143.0
offset_top = -44.5
offset_right = 143.0
offset_bottom = 85.5
grow_horizontal = 2
grow_vertical = 2
text = "Custom scenes can only be viewed in \"Full mode\" if they are in @tool mode and override _get_covered_rect"
horizontal_alignment = 1
vertical_alignment = 1
autowrap_mode = 3
metadata/_edit_layout_mode = 1
[node name="PreviewReal" type="CenterContainer" parent="Split/RightSection/PortraitPreviewSection"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -302.0
offset_top = -139.0
offset_right = 302.0
grow_horizontal = 2
grow_vertical = 0
metadata/_edit_layout_mode = 1
[node name="Control" type="Control" parent="Split/RightSection/PortraitPreviewSection/PreviewReal"]
layout_mode = 2
[node name="RealSizeRemotePivotTransform" type="RemoteTransform2D" parent="Split/RightSection/PortraitPreviewSection/PreviewReal/Control"]
unique_name_in_owner = true
remote_path = NodePath("../../../Node2D/RealPreviewPivot")
update_position = false
update_rotation = false
update_scale = false
[node name="FullPreviewAvailableRect" type="Control" parent="Split/RightSection/PortraitPreviewSection"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 10.0
offset_top = 13.0
offset_right = -10.0
offset_bottom = -16.0
grow_horizontal = 2
grow_vertical = 2
metadata/_edit_layout_mode = 1
[node name="PreviewLabel" type="Label" parent="Split/RightSection/PortraitPreviewSection"]
unique_name_in_owner = true
layout_mode = 0
offset_right = 40.0
offset_bottom = 23.0
text = "No portrait to preview."
[node name="PreviewMode" type="OptionButton" parent="Split/RightSection/PortraitPreviewSection"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -121.0
offset_top = 9.0
offset_right = -9.0
offset_bottom = 40.0
grow_horizontal = 0
item_count = 2
selected = 0
popup/item_0/text = "Full View"
popup/item_0/id = 0
popup/item_1/text = "Real Size"
popup/item_1/id = 1
metadata/_edit_layout_mode = 1
[node name="TextureRect" type="TextureRect" parent="Split/RightSection/PortraitPreviewSection"]
layout_mode = 0
offset_top = -12.0
offset_right = 597.0
offset_bottom = -12.0
[node name="PortraitSettingsSection" type="TabContainer" parent="Split/RightSection"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 0.3
theme_override_constants/side_margin = 5
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
[node name="Image" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
custom_minimum_size = Vector2(0, 35)
layout_mode = 2
horizontal_scroll_mode = 0
script = ExtResource("2_cusvj")
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Image"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="GridContainer" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Image/Flow"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 2
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer"]
layout_mode = 2
text = "Image: "
[node name="ImagePicker" parent="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer" instance=ExtResource("4_a4bcu")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
file_filter = "*.png, *.svg"
resource_icon = SubResource("ImageTexture_uf4x7")
[node name="Scene" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
visible = false
custom_minimum_size = Vector2(0, 35)
layout_mode = 2
horizontal_scroll_mode = 0
script = ExtResource("4_6tltp")
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Scene"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="GridContainer" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 2
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
layout_mode = 2
text = "Scene: "
[node name="ScenePicker" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer" instance=ExtResource("4_a4bcu")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
file_filter = "*.tscn"
placeholder = "Default scene"
resource_icon = SubResource("ImageTexture_uf4x7")
[node name="Label2" type="Label" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
layout_mode = 2
text = "Ignore scale: "
[node name="IgnoreScale" type="CheckBox" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="Layout" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
visible = false
custom_minimum_size = Vector2(0, 35)
layout_mode = 2
horizontal_scroll_mode = 0
script = ExtResource("5_eicvp")
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Layout"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="HBoxContainer" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
layout_mode = 2
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer"]
layout_mode = 2
text = "Scale:"
[node name="PortraitScale" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
value = 100.0
allow_greater = true
suffix = "%"
[node name="HBoxContainer2" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
layout_mode = 2
[node name="Label2" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
layout_mode = 2
text = "Offset:"
[node name="PortraitOffsetX" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
allow_greater = true
allow_lesser = true
suffix = "X"
[node name="PortraitOffsetY" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
allow_greater = true
allow_lesser = true
suffix = "Y"
[node name="MirrorOption" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
layout_mode = 2
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption"]
layout_mode = 2
text = "Mirror:"
[node name="PortraitMirror" type="CheckBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption"]
unique_name_in_owner = true
layout_mode = 2
[node name="Exports" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
visible = false
custom_minimum_size = Vector2(0, 35)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_81xk6")
horizontal_scroll_mode = 0
script = ExtResource("7_4enie")
[node name="Grid" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Exports"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/h_separation = 10
columns = 2
[connection signal="button_clicked" from="Split/Editor/PortraitListSection/Portraits/Panel/PortraitTree" to="." method="_on_portrait_tree_button_clicked"]
[connection signal="resized" from="Split/RightSection/PortraitPreviewSection/FullPreviewAvailableRect" to="." method="_on_full_preview_available_rect_resized"]
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer/ImagePicker" to="Split/RightSection/PortraitSettingsSection/Image" method="_on_image_picker_value_changed"]
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer/ScenePicker" to="Split/RightSection/PortraitSettingsSection/Scene" method="_on_scene_picker_value_changed"]
[connection signal="toggled" from="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer/IgnoreScale" to="Split/RightSection/PortraitSettingsSection/Scene" method="_on_ignore_scale_toggled"]
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer/PortraitScale" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_scale_value_changed"]
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2/PortraitOffsetX" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_offset_x_value_changed"]
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2/PortraitOffsetY" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_offset_y_value_changed"]
[connection signal="toggled" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption/PortraitMirror" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_mirror_toggled"]

View file

@ -0,0 +1,20 @@
@tool
class_name DialogicCharacterEditorMainTab
extends Control
## Base class for all character editor main tabs. Methods should be overriden.
# Emit this, if something changed
signal changed
var character_editor:Control
func _load_character(resource:DialogicCharacter) -> void:
pass
func _save_changes(resource:DialogicCharacter) -> DialogicCharacter:
return resource

View file

@ -0,0 +1,16 @@
@tool
class_name DialogicCharacterEditorPortraitSettingsTab
extends ScrollContainer
## Base class for all portrait settings tabs. Methods should be overriden.
# Emit this, if something changed
signal changed
signal update_preview
var character_editor:Control
var selected_item :TreeItem = null
func _load_portrait_data(data:Dictionary) -> void:
pass

View file

@ -0,0 +1,115 @@
@tool
extends Tree
## Tree that displays the portrait list as a hirarchy
var editor := find_parent('Character Editor')
var current_group_nodes := {}
func clear_tree() -> void:
clear()
current_group_nodes = {}
func add_portrait_item(portrait_name:String, portrait_data:Dictionary, parent_item:TreeItem) -> TreeItem:
var item :TreeItem = %PortraitTree.create_item(parent_item)
item.set_text(0, portrait_name)
item.set_metadata(0, portrait_data)
if portrait_name == editor.current_resource.default_portrait:
item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default')
item.add_button(0, get_theme_icon('Duplicate', 'EditorIcons'), 3, false, 'Duplicate')
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
return item
func add_portrait_group(goup_name:String = "Group", parent_item:TreeItem = get_root()):
var item :TreeItem = %PortraitTree.create_item(parent_item)
item.set_icon(0, get_theme_icon("Groups", "EditorIcons"))
item.set_text(0, goup_name)
item.set_metadata(0, {'group':true})
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
return item
func get_full_item_name(item:TreeItem) -> String:
var item_name := item.get_text(0)
while item.get_parent() != get_root() and item != get_root():
item_name = item.get_parent().get_text(0)+"/"+item_name
item = item.get_parent()
return item_name
# Will create all not yet existing folders in the given path.
# Returns the last folder (the parent of the portrait item of this path).
func create_necessary_group_items(path:String) -> void:
var last_item := get_root()
var item_path := ""
for i in Array(path.split('/')).slice(0, -1):
item_path += "/"+i
item_path = item_path.trim_prefix('/')
if current_group_nodes.has(item_path+"/"+i):
last_item = current_group_nodes[item_path+"/"+i]
else:
var new_item := add_portrait_group(i, last_item)
current_group_nodes[item_path+"/"+i] = new_item
last_item = new_item
return last_item
################################################################################
## DRAG AND DROP
################################################################################
func _get_drag_data(position:Vector2) -> Variant:
set_drop_mode_flags(DROP_MODE_ON_ITEM)
var preview := Label.new()
preview.text = " "+get_selected().get_text(0)
preview.add_theme_stylebox_override('normal', get_theme_stylebox("Background", "EditorStyles"))
set_drag_preview(preview)
return get_selected()
func _can_drop_data(position:Vector2, data:Variant) -> bool:
return data is TreeItem
func _drop_data(position:Vector2, item:Variant) -> void:
var to_item := get_item_at_position(position)
if to_item:
var test_item:= to_item
while true:
if test_item == item:
return
test_item = test_item.get_parent()
if test_item == get_root():
break
var parent := get_root()
if to_item:
parent = to_item.get_parent()
if to_item and to_item.get_metadata(0).has('group'):
parent = to_item
var new_item := copy_branch_or_item(item, parent)
if to_item and !to_item.get_metadata(0).has('group'):
new_item.move_after(to_item)
item.free()
func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem:
var new_item :TreeItem = null
if item.get_metadata(0).has('group'):
new_item = add_portrait_group(item.get_text(0), new_parent)
else:
new_item = add_portrait_item(item.get_text(0), item.get_metadata(0), new_parent)
for child in item.get_children():
copy_branch_or_item(child, new_item)
return new_item

View file

@ -0,0 +1,102 @@
@tool
extends DialogicCharacterEditorPortraitSettingsTab
## Tab that allows setting values of exported scene variables
## for custom portrait scenes
var current_portrait_data := {}
func _ready() -> void:
get_parent().set_tab_icon(get_index(), get_theme_icon("EditInternal", "EditorIcons"))
add_theme_stylebox_override('panel', get_theme_stylebox("Background", "EditorStyles"))
func _load_portrait_data(data:Dictionary) -> void:
if data.get('scene', '').is_empty():
get_parent().set_tab_hidden(get_index(), true)
else:
get_parent().set_tab_hidden(get_index(), false)
current_portrait_data = data
load_portrait_scene_export_variables()
func load_portrait_scene_export_variables():
var scene = null
if !current_portrait_data.get('scene', '').is_empty():
scene = load(current_portrait_data.get('scene'))
if !scene:
return
for child in $Grid.get_children():
child.queue_free()
scene = scene.instantiate()
for i in scene.script.get_script_property_list():
if i['usage'] & PROPERTY_USAGE_EDITOR:
var label = Label.new()
label.text = i['name']
label.add_theme_stylebox_override('normal', get_theme_stylebox("CanvasItemInfoOverlay", "EditorStyles"))
label.size_flags_horizontal = SIZE_EXPAND_FILL
$Grid.add_child(label)
var input = null
var current_value :String = ""
if current_portrait_data.has('export_overrides') and current_portrait_data['export_overrides'].has(i['name']):
current_value = current_portrait_data['export_overrides'][i['name']]
match typeof(scene.get(i['name'])):
TYPE_BOOL:
input = CheckBox.new()
if current_value:
input.button_pressed = current_value == "true"
input.toggled.connect(_on_export_bool_submitted.bind(i['name']))
TYPE_COLOR:
input = ColorPickerButton.new()
if current_value:
input.color = str_to_var(current_value)
input.color_changed.connect(_on_export_color_submitted.bind(i['name']))
input.custom_minimum_size.x = DialogicUtil.get_editor_scale()*50
TYPE_INT:
if i['hint'] & PROPERTY_HINT_ENUM:
input = OptionButton.new()
for x in i['hint_string'].split(','):
input.add_item(x)
input.item_selected.connect(_on_export_int_enum_submitted.bind(i['name']))
_:
input = LineEdit.new()
if current_value:
input.text = current_value
input.text_submitted.connect(_on_export_input_text_submitted.bind(i['name']))
input.size_flags_horizontal = SIZE_EXPAND_FILL
$Grid.add_child(input)
if i['usage'] & PROPERTY_USAGE_GROUP:
var title := Label.new()
title.text = i['name']
title.add_theme_stylebox_override('normal', get_theme_stylebox("ContextualToolbar", "EditorStyles"))
$Grid.add_child(title)
$Grid.add_child(Control.new())
func set_export_override(property_name:String, value:String = "") -> void:
var data:Dictionary = selected_item.get_metadata(0)
if !data.has('export_overrides'):
data['export_overrides'] = {}
if !value.is_empty():
data['export_overrides'][property_name] = value
else:
data['export_overrides'].erase(property_name)
changed.emit()
update_preview.emit()
func _on_export_input_text_submitted(text:String, property_name:String) -> void:
set_export_override(property_name, var_to_str(text))
func _on_export_bool_submitted(value:bool, property_name:String) -> void:
set_export_override(property_name, var_to_str(value))
func _on_export_color_submitted(color:Color, property_name:String) -> void:
set_export_override(property_name, var_to_str(color))
func _on_export_int_enum_submitted(item:int, property_name:String) -> void:
set_export_override(property_name, var_to_str(item))

View file

@ -0,0 +1,25 @@
@tool
extends DialogicCharacterEditorPortraitSettingsTab
## Tab that allows setting an image file on a portrait.
func _ready() -> void:
get_parent().set_tab_icon(get_index(), get_theme_icon('Image', 'EditorIcons'))
%ImagePicker.file_filter = "*.png, *.svg"
%ImagePicker.resource_icon = get_theme_icon('Image', 'EditorIcons')
func _load_portrait_data(data:Dictionary) -> void:
# hides/shows this tab based on the scene value of this portrait
# (only shown if the default scene is used)
get_parent().set_tab_hidden(get_index(), !data.get('scene', '').is_empty())
%ImagePicker.set_value(data.get('image', ''))
func _on_image_picker_value_changed(prop_name:String, value:String):
var data:Dictionary = selected_item.get_metadata(0)
data['image'] = value
changed.emit()
update_preview.emit()

View file

@ -0,0 +1,43 @@
@tool
extends DialogicCharacterEditorPortraitSettingsTab
## Tab that allows setting size, offset and mirror of a portrait.
func _ready():
get_parent().set_tab_icon(get_index(), get_theme_icon('EditorPivot', 'EditorIcons'))
func _load_portrait_data(data:Dictionary) -> void:
%PortraitScale.value = data.get('scale', 1.0)*100
%PortraitOffsetX.value = data.get('offset', Vector2()).x
%PortraitOffsetY.value = data.get('offset', Vector2()).y
%PortraitMirror.button_pressed = data.get('mirror', false)
func _on_portrait_scale_value_changed(value) -> void:
var data:Dictionary = selected_item.get_metadata(0)
data['scale'] = value/100.0
update_preview.emit()
changed.emit()
func _on_portrait_offset_x_value_changed(value) -> void:
var data:Dictionary = selected_item.get_metadata(0)
data['offset'] = data.get('offset', Vector2())
data['offset'].x = value
update_preview.emit()
changed.emit()
func _on_portrait_offset_y_value_changed(value)-> void:
var data:Dictionary = selected_item.get_metadata(0)
data['offset'] = data.get('offset', Vector2())
data['offset'].y = value
update_preview.emit()
changed.emit()
func _on_portrait_mirror_toggled(button_pressed:bool)-> void:
var data:Dictionary = selected_item.get_metadata(0)
data['mirror'] = button_pressed
update_preview.emit()
changed.emit()

View file

@ -0,0 +1,30 @@
@tool
extends DialogicCharacterEditorPortraitSettingsTab
## Tab that allows setting a custom scene.
func _ready():
get_parent().set_tab_icon(get_index(), get_theme_icon('PackedScene', 'EditorIcons'))
%ScenePicker.file_filter = "*.tscn"
%ScenePicker.resource_icon = get_theme_icon('PackedScene', 'EditorIcons')
%ScenePicker.placeholder = 'Default scene'
func _load_portrait_data(data:Dictionary) -> void:
%ScenePicker.set_value(data.get('scene', ''))
%IgnoreScale.button_pressed = data.get('ignore_char_scale', false)
func _on_scene_picker_value_changed(prop_name:String, value:String) -> void:
var data:Dictionary = selected_item.get_metadata(0)
data['scene'] = value
update_preview.emit()
changed.emit()
func _on_ignore_scale_toggled(button_pressed):
var data:Dictionary = selected_item.get_metadata(0)
data['ignore_char_scale'] = button_pressed
update_preview.emit()
changed.emit()

View file

@ -0,0 +1,36 @@
@tool
extends DialogicCharacterEditorMainTab
## The general character settings tab
func _ready() -> void:
# Connecting all necessary signals
%ColorPickerButton.color_changed.connect(character_editor.something_changed)
%DisplayNameLineEdit.text_changed.connect(character_editor.something_changed)
%NicknameLineEdit.text_changed.connect(character_editor.something_changed)
%DescriptionTextEdit.text_changed.connect(character_editor.something_changed)
func _load_character(resource:DialogicCharacter) -> void:
%DisplayNameLineEdit.text = resource.display_name
%ColorPickerButton.color = resource.color
%NicknameLineEdit.text = ""
for nickname in resource.nicknames:
%NicknameLineEdit.text += nickname +", "
%NicknameLineEdit.text = %NicknameLineEdit.text.trim_suffix(', ')
%DescriptionTextEdit.text = resource.description
func _save_changes(resource:DialogicCharacter) -> DialogicCharacter:
resource.display_name = %DisplayNameLineEdit.text
resource.color = %ColorPickerButton.color
var nicknames := []
for n_name in %NicknameLineEdit.text.split(','):
nicknames.append(n_name.strip_edges())
resource.nicknames = nicknames
resource.description = %DescriptionTextEdit.text
return resource

View file

@ -0,0 +1,67 @@
[gd_scene load_steps=2 format=3 uid="uid://de5kj4vd0we1w"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_tab_general.gd" id="1_3e1i1"]
[node name="General" type="GridContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 7.5
offset_top = 38.5
offset_right = -7.5
offset_bottom = -7.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/h_separation = 1
theme_override_constants/v_separation = 6
columns = 2
script = ExtResource("1_3e1i1")
[node name="Label2" type="Label" parent="."]
layout_mode = 2
text = "Display Name: "
[node name="DisplayName" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
[node name="DisplayNameLineEdit" type="LineEdit" parent="DisplayName"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "This name will be displayed on the name label. You can use a dialogic variable. E.g. :{Player.name}"
expand_to_text_length = true
caret_blink = true
caret_blink_interval = 0.5
[node name="ColorPickerButton" type="ColorPickerButton" parent="DisplayName"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
tooltip_text = "This color can be used on the name label and for occurences of the characters name in text (autocolor names)."
color = Color(1, 1, 1, 1)
edit_alpha = false
[node name="Label3" type="Label" parent="."]
layout_mode = 2
text = "Nicknames:"
[node name="NicknameLineEdit" type="LineEdit" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "If autocolor names is enabled, these will be colored in the characters color as well."
caret_blink = true
caret_blink_interval = 0.5
[node name="Label4" type="Label" parent="."]
layout_mode = 2
size_flags_vertical = 0
text = "Description:"
[node name="DescriptionTextEdit" type="TextEdit" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "No effect, just for you."
wrap_mode = 1
scroll_fit_content_height = true