First work on dialogic, resized guild, and started implementing portraits.

This commit is contained in:
2025-08-14 10:26:24 -04:00
parent 95a7db036b
commit 3aeb3d44e6
959 changed files with 47688 additions and 46 deletions

View File

@@ -0,0 +1,16 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(node, 'position:y', base_position.y-node.get_viewport().size.y/10, time*0.4).set_trans(Tween.TRANS_EXPO)
tween.parallel().tween_property(node, 'scale:y', base_scale.y*1.05, time*0.4).set_trans(Tween.TRANS_EXPO)
tween.tween_property(node, 'position:y', base_position.y, time*0.6).set_trans(Tween.TRANS_BOUNCE)
tween.parallel().tween_property(node, 'scale:y', base_scale.y, time*0.6).set_trans(Tween.TRANS_BOUNCE)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"bounce": {"type": AnimationType.ACTION},
}

View File

@@ -0,0 +1 @@
uid://cxjbflqo8ydvj

View File

@@ -0,0 +1,39 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
var end_scale: Vector2 = node.scale
var end_modulate_alpha := 1.0
var modulation_property := get_modulation_property()
if is_reversed:
end_scale = Vector2(0, 0)
end_modulate_alpha = 0.0
else:
node.scale = Vector2(0, 0)
var original_modulation: Color = node.get(modulation_property)
original_modulation.a = 0.0
node.set(modulation_property, original_modulation)
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_trans(Tween.TRANS_SINE)
tween.set_parallel()
(tween.tween_property(node, "scale", end_scale, time)
.set_trans(Tween.TRANS_SPRING)
.set_ease(Tween.EASE_OUT))
tween.tween_property(node, modulation_property + ":a", end_modulate_alpha, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"bounce in": {"reversed": false, "type": AnimationType.IN},
"bounce out": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://ycvv3pukkwcq

View File

@@ -0,0 +1,44 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
var start_height: float = base_position.y - node.get_viewport().size.y / 5
var end_height := base_position.y
var start_modulation := 0.0
var end_modulation := 1.0
if is_reversed:
end_height = start_height
start_height = base_position.y
end_modulation = 0.0
start_modulation = 1.0
node.position.y = start_height
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_SINE)
tween.set_parallel()
var end_postion := Vector2(base_position.x, end_height)
tween.tween_property(node, "position", end_postion, time)
var property := get_modulation_property()
var original_modulation: Color = node.get(property)
original_modulation.a = start_modulation
node.set(property, original_modulation)
var modulation_alpha := property + ":a"
tween.tween_property(node, modulation_alpha, end_modulation, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"fade in down": {"reversed": false, "type": AnimationType.IN},
"fade out up": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://dk47pnk3okpls

View File

@@ -0,0 +1,34 @@
extends DialogicAnimation
func animate() -> void:
var modulation_property := get_modulation_property()
var end_modulation_alpha := 1.0
if is_reversed:
end_modulation_alpha = 0.0
else:
var original_modulation: Color = node.get(modulation_property)
original_modulation.a = 0.0
node.set(modulation_property, original_modulation)
var tween := (node.create_tween() as Tween)
if is_reversed:
tween.set_ease(Tween.EASE_IN)
else:
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_SINE)
tween.tween_property(node, modulation_property + ":a", end_modulation_alpha, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"fade in": {"reversed": false, "type": AnimationType.IN},
"fade out": {"reversed": true, "type": AnimationType.OUT},
"fade cross": {"type": AnimationType.CROSSFADE},
}

View File

@@ -0,0 +1 @@
uid://cw4f0q7bbh3be

View File

@@ -0,0 +1,44 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
var start_height: float = base_position.y + node.get_viewport().size.y / 5
var end_height := base_position.y
var start_modulation := 0.0
var end_modulation := 1.0
if is_reversed:
end_height = start_height
start_height = base_position.y
end_modulation = 0.0
start_modulation = 1.0
node.position.y = start_height
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_SINE)
tween.set_parallel()
var end_postion := Vector2(base_position.x, end_height)
tween.tween_property(node, "position", end_postion, time)
var property := get_modulation_property()
var original_modulation: Color = node.get(property)
original_modulation.a = start_modulation
node.set(property, original_modulation)
var modulation_alpha := property + ":a"
tween.tween_property(node, modulation_alpha, end_modulation, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"fade in up": {"reversed": false, "type": AnimationType.IN},
"fade out down": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://dxc52wjbrwfww

View File

@@ -0,0 +1,13 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.tween_property(node, 'scale', Vector2(1,1)*1.2, time*0.5).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
tween.tween_property(node, 'scale', Vector2(1,1), time*0.5).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"heartbeat": {"type": AnimationType.ACTION},
}

View File

@@ -0,0 +1 @@
uid://w5n6c761qi70

View File

@@ -0,0 +1,12 @@
extends DialogicAnimation
func animate() -> void:
await node.get_tree().process_frame
finished.emit()
func _get_named_variations() -> Dictionary:
return {
"instant in": {"reversed": false, "type": AnimationType.IN},
"instant out": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://dq6qd77wqtuak

View File

@@ -0,0 +1,20 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
var strength: float = node.get_viewport().size.x/60
var bound_multitween := DialogicUtil.multitween.bind(node, "position", "animation_shake_x")
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.2)
tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1)
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.1)
tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1)
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.1)
tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1)
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.2)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"shake x": {"type": AnimationType.ACTION},
}

View File

@@ -0,0 +1 @@
uid://d2m8ruax5nid5

View File

@@ -0,0 +1,23 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
var strength: float = node.get_viewport().size.y/40
tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.2)
tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1)
tween.tween_property(node, 'position:y', base_position.y, time * 0.2)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"shake y": {"type": AnimationType.ACTION},
}

View File

@@ -0,0 +1 @@
uid://bjc3ljt064o5c

View File

@@ -0,0 +1,26 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
var target_position := base_position.y
var start_position: float = -node.get_viewport().size.y
if is_reversed:
target_position = -node.get_viewport().size.y
start_position = base_position.y
node.position.y = start_position
tween.tween_property(node, 'position:y', target_position, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"slide in down": {"reversed": false, "type": AnimationType.IN},
"slide out up": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://dg0cged7g0gby

View File

@@ -0,0 +1,26 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
var end_position_x: float = base_position.x
if is_reversed:
end_position_x = -node.get_viewport().size.x / 2
else:
node.position.x = -node.get_viewport().size.x / 5
tween.tween_property(node, 'position:x', end_position_x, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"slide in left": {"reversed": false, "type": AnimationType.IN},
"slide out right": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://b0nuamcfgq6pr

View File

@@ -0,0 +1,27 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
var viewport_x: float = node.get_viewport().size.x
var start_position_x: float = viewport_x + viewport_x / 5
var end_position_x := base_position.x
if is_reversed:
start_position_x = base_position.x
end_position_x = viewport_x + node.get_viewport().size.x / 5
node.position.x = start_position_x
tween.tween_property(node, 'position:x', end_position_x, time)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"slide in right": {"reversed": false, "type": AnimationType.IN},
"slide out left": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://cp530lw7pc54s

View File

@@ -0,0 +1,25 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
var start_position_y: float = node.get_viewport().size.y * 2
var end_position_y := base_position.y
if is_reversed:
start_position_y = base_position.y
end_position_y = node.get_viewport().size.y * 2
node.position.y = start_position_y
tween.tween_property(node, 'position:y', end_position_y, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"slide in up": {"reversed": false, "type": AnimationType.IN},
"slide out down": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://l77151y81bnk

View File

@@ -0,0 +1,25 @@
extends DialogicAnimation
func animate() -> void:
var tween := (node.create_tween() as Tween)
tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
var strength: float = 0.01
tween.set_parallel(true)
tween.tween_property(node, 'scale', Vector2(1,1)*(1+strength), time*0.3)
tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.2)
tween.tween_property(node, 'rotation', strength, time*0.1).set_delay(time*0.3)
tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.4)
tween.tween_property(node, 'rotation', strength, time*0.1).set_delay(time*0.5)
tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.6)
tween.chain().tween_property(node, 'scale', Vector2(1,1), time*0.3)
tween.parallel().tween_property(node, 'rotation', 0.0, time*0.3)
tween.finished.connect(emit_signal.bind('finished_once'))
func _get_named_variations() -> Dictionary:
return {
"tada": {"type": AnimationType.ACTION},
}

View File

@@ -0,0 +1 @@
uid://crvqdyahfjgw2

View File

@@ -0,0 +1,36 @@
extends DialogicAnimation
func animate() -> void:
var modulate_property := get_modulation_property()
var modulate_alpha_property := modulate_property + ":a"
var end_scale: Vector2 = node.scale
var end_modulation_alpha := 1.0
if is_reversed:
end_modulation_alpha = 0.0
else:
node.scale = Vector2(0, 0)
node.position.y = base_position.y - node.get_viewport().size.y * 0.5
var original_modulation: Color = node.get(modulate_property)
original_modulation.a = 0.0
node.set(modulate_property, original_modulation)
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_EXPO)
tween.set_parallel(true)
tween.tween_property(node, "scale", end_scale, time)
tween.tween_property(node, "position", base_position, time)
tween.tween_property(node, modulate_alpha_property, end_modulation_alpha, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"zoom center in": {"reversed": false, "type": AnimationType.IN},
"zoom center out": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://b6etvhp8jjomy

View File

@@ -0,0 +1,35 @@
extends DialogicAnimation
func animate() -> void:
var modulate_property := get_modulation_property()
var modulate_alpha_property := modulate_property + ":a"
var end_scale: Vector2 = node.scale
var end_modulation_alpha := 1.0
if is_reversed:
end_scale = Vector2(0, 0)
end_modulation_alpha = 0.0
else:
node.scale = Vector2(0,0)
var original_modulation: Color = node.get(modulate_property)
original_modulation.a = 0.0
node.set(modulate_property, original_modulation)
var tween := (node.create_tween() as Tween)
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO)
tween.set_parallel(true)
tween.tween_property(node, "scale", end_scale, time)
tween.tween_property(node, modulate_alpha_property, end_modulation_alpha, time)
await tween.finished
finished_once.emit()
func _get_named_variations() -> Dictionary:
return {
"zoom in": {"reversed": false, "type": AnimationType.IN},
"zoom out": {"reversed": true, "type": AnimationType.OUT},
}

View File

@@ -0,0 +1 @@
uid://jbpknfsqxd1o

View File

@@ -0,0 +1,47 @@
@tool
class_name DialogicPortraitAnimationUtil
enum AnimationType {ALL=-1, IN=1, OUT=2, ACTION=3, CROSSFADE=4}
static func guess_animation(string:String, type := AnimationType.ALL) -> String:
var default := {}
var filter := {}
var ignores := []
match type:
AnimationType.ALL:
pass
AnimationType.IN:
filter = {"type":AnimationType.IN}
ignores = ["in"]
AnimationType.OUT:
filter = {"type":AnimationType.OUT}
ignores = ["out"]
AnimationType.ACTION:
filter = {"type":AnimationType.ACTION}
AnimationType.CROSSFADE:
filter = {"type":AnimationType.CROSSFADE}
ignores = ["cross"]
return DialogicResourceUtil.guess_special_resource(&"PortraitAnimation", string, default, filter, ignores).get("path", "")
static func get_portrait_animations_filtered(type := AnimationType.ALL) -> Dictionary:
var filter := {"type":type}
if type == AnimationType.ALL:
filter["type"] = [AnimationType.IN, AnimationType.OUT, AnimationType.ACTION]
return DialogicResourceUtil.list_special_resources("PortraitAnimation", filter)
static func get_suggestions(_search_text := "", current_value:= "", empty_text := "Default", action := AnimationType.ALL) -> Dictionary:
var suggestions := {}
if empty_text and current_value:
suggestions[empty_text] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
for anim_name in get_portrait_animations_filtered(action):
suggestions[DialogicUtil.pretty_name(anim_name)] = {
'value' : DialogicUtil.pretty_name(anim_name),
'editor_icon' : ["Animation", "EditorIcons"]
}
return suggestions

View File

@@ -0,0 +1 @@
uid://ctnhk0vfhcq5q

View File

@@ -0,0 +1,77 @@
class_name DialogicAnimation
extends Node
## Class that can be used to animate portraits. Can be extended to create animations.
enum AnimationType {IN=1, OUT=2, ACTION=3, CROSSFADE=4}
signal finished_once
signal finished
## Set at runtime, will be the node to animate.
var node: Node
## Set at runtime, will be the length of the animation.
var time: float
## Set at runtime, will be the base position of the node.
## Depending on the animation, this might be the start, end or both.
var base_position: Vector2
## Set at runtime, will be the base scale of the node.
var base_scale: Vector2
## Used to repeate the animation for a number of times.
var repeats: int
## If `true`, the animation will be reversed.
## This must be implemented by each animation or it will have no effect.
var is_reversed: bool = false
func _ready() -> void:
finished_once.connect(finished_one_loop)
## To be overridden. Do the actual animating/tweening in here.
## Use the properties [member node], [member time], [member base_position], etc.
func animate() -> void:
pass
## This method controls whether to repeat the animation or not.
## Animations must call this once they finished an animation.
func finished_one_loop() -> void:
repeats -= 1
if repeats > 0:
animate()
else:
finished.emit()
func pause() -> void:
if node:
node.process_mode = Node.PROCESS_MODE_DISABLED
func resume() -> void:
if node:
node.process_mode = Node.PROCESS_MODE_INHERIT
func _get_named_variations() -> Dictionary:
return {}
## If the animation wants to change the modulation, this method
## will return the property to change.
##
## The [class CanvasGroup] can use `self_modulate` instead of `modulate`
## to uniformly change the modulation of all children without additively
## overlaying the modulations.
func get_modulation_property() -> String:
if node is CanvasGroup:
return "self_modulate"
else:
return "modulate"

View File

@@ -0,0 +1 @@
uid://ccyk44gg2r0w2

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5tu88x32sjkf"
path="res://.godot/imported/custom_portrait_thumbnail.png-0513583853d87342a634d56bc0bec965.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/custom_portrait_thumbnail.png"
dest_files=["res://.godot/imported/custom_portrait_thumbnail.png-0513583853d87342a634d56bc0bec965.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,15 @@
@tool
extends DialogicPortrait
## Default portrait scene.
## The parent class has a character and portrait variable.
@export_group('Main')
@export_file var image := ""
## Load anything related to the given character and portrait
func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void:
apply_character_and_portrait(passed_character, passed_portrait)
apply_texture($Portrait, image)

View File

@@ -0,0 +1 @@
uid://br7r46x628pxp

View File

@@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://b32paf0ll6um8"]
[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/default_portrait.gd" id="1_wn77n"]
[node name="DefaultPortrait" type="Node2D"]
script = ExtResource("1_wn77n")
[node name="Portrait" type="Sprite2D" parent="."]
centered = false

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b0eisk30btlx2"
path="res://.godot/imported/default_portrait_thumbnail.png-f9f92adb946f409def18655d6ab5c5df.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/default_portrait_thumbnail.png"
dest_files=["res://.godot/imported/default_portrait_thumbnail.png-f9f92adb946f409def18655d6ab5c5df.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,121 @@
class_name DialogicPortrait
extends Node
## Default portrait class. Should be extended by custom portraits.
## Stores the character that this scene displays.
var character: DialogicCharacter
## Stores the name of the current portrait.
var portrait: String
#region MAIN OVERRIDES
################################################################################
## This function can be overridden.
## If this returns true, it won't instance a new scene, but call
## [method _update_portrait] on this one.
## This is only relevant if the next portrait uses the same scene.
## This allows implementing transitions between portraits that use the same scene.
func _should_do_portrait_update(_character: DialogicCharacter, _portrait: String) -> bool:
return false
## If the custom portrait accepts a change, then accept it here
## You should position your portrait so that the root node is at the pivot point*.
## For example for a simple sprite this code would work:
## >>> $Sprite.position = $Sprite.get_rect().size * Vector2(-0.5, -1)
##
## * this depends on the portrait containers, but it will most likely be the bottom center (99% of cases)
func _update_portrait(_passed_character: DialogicCharacter, _passed_portrait: String) -> void:
pass
## This should be implemented. It is used for sizing in the
## character editor preview and in portrait containers.
## Scale and offset will be applied by Dialogic.
## For example, a simple sprite:
## >>> return Rect2($Sprite.position, $Sprite.get_rect().size)
##
## This will only work as expected if the portrait is positioned so that the
## root is at the pivot point.
##
## If you've used apply_texture this should work automatically.
func _get_covered_rect() -> Rect2:
if has_meta('texture_holder_node') and get_meta('texture_holder_node', null) != null and is_instance_valid(get_meta('texture_holder_node')):
var node: Node = get_meta('texture_holder_node')
if node is Sprite2D or node is TextureRect:
return Rect2(node.position, node.get_rect().size)
return Rect2()
## If implemented, this is called when the mirror changes
func _set_mirror(mirror:bool) -> void:
if has_meta('texture_holder_node') and get_meta('texture_holder_node', null) != null and is_instance_valid(get_meta('texture_holder_node')):
var node: Node = get_meta('texture_holder_node')
if node is Sprite2D or node is TextureRect:
node.flip_h = mirror
## Function to accept and use the extra data, if the custom portrait wants to accept it
func _set_extra_data(_data: String) -> void:
pass
#endregion
#region HIGHLIGHT OVERRIDES
################################################################################
## Called when this becomes the active speaker
func _highlight() -> void:
pass
## Called when this stops being the active speaker
func _unhighlight() -> void:
pass
#endregion
#region HELPERS
################################################################################
## Helper that quickly setups and checks the character and portrait.
func apply_character_and_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void:
if passed_portrait == "" or not passed_portrait in passed_character.portraits.keys():
passed_portrait = passed_character.default_portrait
portrait = passed_portrait
character = passed_character
func apply_texture(node:Node, texture_path:String) -> void:
if not character or not character.portraits.has(portrait):
return
if not "texture" in node:
return
node.texture = null
if not ResourceLoader.exists(texture_path):
# This is a leftover from alpha.
# Removing this will break any portraits made before alpha-10
if ResourceLoader.exists(character.portraits[portrait].get('image', '')):
texture_path = character.portraits[portrait].get('image', '')
else:
return
node.texture = load(texture_path)
if node is Sprite2D or node is TextureRect:
if node is Sprite2D:
node.centered = false
node.scale = Vector2.ONE
if node is TextureRect:
if !is_inside_tree():
await ready
node.position = node.get_rect().size * Vector2(-0.5, -1)
set_meta('texture_holder_node', node)
#endregion

View File

@@ -0,0 +1 @@
uid://ddpj5fsa86ksb

View File

@@ -0,0 +1,578 @@
@tool
class_name DialogicCharacterEvent
extends DialogicEvent
## Event that allows to manipulate character portraits.
enum Actions {JOIN, LEAVE, UPDATE}
### Settings
## The type of action of this event (JOIN/LEAVE/UPDATE). See [Actions].
var action := Actions.JOIN
## The character that will join/leave/update.
var character: DialogicCharacter = null
## For Join/Update, this will be the portrait of the character that is shown.
## Not used on Leave.
## If empty, the default portrait will be used.
var portrait := ""
## The index of the position this character should move to
var transform := "center"
## Name of the animation script (extending DialogicAnimation).
## On Join/Leave empty (default) will fallback to the animations set in the settings.
## On Update empty will mean no animation.
var animation_name := ""
## Length of the animation.
var animation_length: float = 0.5
## How often the animation is repeated. Only for Update events.
var animation_repeats: int = 1
## If true, the events waits for the animation to finish before the next event starts.
var animation_wait := false
## The fade animation to use. If left empty, the default cross-fade animation AND time will be used.
var fade_animation := ""
var fade_length := 0.5
## For Update only. If bigger then 0, the portrait will tween to the
## new position (if changed) in this time (in seconds).
var transform_time: float = 0.0
var transform_ease := Tween.EaseType.EASE_IN_OUT
var transform_trans := Tween.TransitionType.TRANS_SINE
var ease_options := [
{'label': 'In', 'value': Tween.EASE_IN},
{'label': 'Out', 'value': Tween.EASE_OUT},
{'label': 'In_Out', 'value': Tween.EASE_IN_OUT},
{'label': 'Out_In', 'value': Tween.EASE_OUT_IN},
]
var trans_options := [
{'label': 'Linear', 'value': Tween.TRANS_LINEAR},
{'label': 'Sine', 'value': Tween.TRANS_SINE},
{'label': 'Quint', 'value': Tween.TRANS_QUINT},
{'label': 'Quart', 'value': Tween.TRANS_QUART},
{'label': 'Quad', 'value': Tween.TRANS_QUAD},
{'label': 'Expo', 'value': Tween.TRANS_EXPO},
{'label': 'Elastic', 'value': Tween.TRANS_ELASTIC},
{'label': 'Cubic', 'value': Tween.TRANS_CUBIC},
{'label': 'Circ', 'value': Tween.TRANS_CIRC},
{'label': 'Bounce', 'value': Tween.TRANS_BOUNCE},
{'label': 'Back', 'value': Tween.TRANS_BACK},
{'label': 'Spring', 'value': Tween.TRANS_SPRING}
]
## The z_index that the portrait should have.
var z_index: int = 0
## If true, the portrait will be set to mirrored.
var mirrored := false
## If set, will be passed to the portrait scene.
var extra_data := ""
### Helpers
## Indicators for whether something should be updated (UPDATE mode only)
var set_portrait := false
var set_transform := false
var set_z_index := false
var set_mirrored := false
## Used to set the character resource from the unique name identifier and vice versa
var character_identifier: String:
get:
if character_identifier == '--All--':
return '--All--'
if character:
var identifier := DialogicResourceUtil.get_unique_identifier(character.resource_path)
if not identifier.is_empty():
return identifier
return character_identifier
set(value):
character_identifier = value
character = DialogicResourceUtil.get_character_resource(value)
if character and not character.portraits.has(portrait):
portrait = ""
ui_update_needed.emit()
var regex := RegEx.create_from_string(r'(?<type>join|update|leave)\s*(")?(?<name>(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*\((?<portrait>.*)\))?(\s*(?<transform>[^\[]*))?(\s*\[(?<shortcode>.*)\])?')
################################################################################
## EXECUTION
################################################################################
func _execute() -> void:
if not character and not character_identifier == "--All--":
finish()
return
# Calculate animation time (can be shortened during skipping)
var final_animation_length: float = animation_length
var final_position_move_time: float = transform_time
if dialogic.Inputs.auto_skip.enabled:
var max_time: float = dialogic.Inputs.auto_skip.time_per_event
final_animation_length = min(max_time, animation_length)
final_position_move_time = min(max_time, transform_time)
# JOIN -------------------------------------
if action == Actions.JOIN:
if dialogic.has_subsystem('History') and !dialogic.Portraits.is_character_joined(character):
var character_name_text := dialogic.Text.get_character_name_parsed(character)
dialogic.History.store_simple_history_entry(character_name_text + " joined", event_name, {'character': character_name_text, 'mode':'Join'})
await dialogic.Portraits.join_character(
character, portrait, transform,
mirrored, z_index, extra_data,
animation_name, final_animation_length, animation_wait)
# LEAVE -------------------------------------
elif action == Actions.LEAVE:
if character_identifier == '--All--':
if dialogic.has_subsystem('History') and len(dialogic.Portraits.get_joined_characters()):
dialogic.History.store_simple_history_entry("Everyone left", event_name, {'character': "All", 'mode':'Leave'})
await dialogic.Portraits.leave_all_characters(
animation_name,
final_animation_length,
animation_wait
)
elif character:
if dialogic.has_subsystem('History') and dialogic.Portraits.is_character_joined(character):
var character_name_text := dialogic.Text.get_character_name_parsed(character)
dialogic.History.store_simple_history_entry(character_name_text+" left", event_name, {'character': character_name_text, 'mode':'Leave'})
await dialogic.Portraits.leave_character(
character,
animation_name,
final_animation_length,
animation_wait
)
# UPDATE -------------------------------------
elif action == Actions.UPDATE:
if not character or not dialogic.Portraits.is_character_joined(character):
finish()
return
dialogic.Portraits.change_character_extradata(character, extra_data)
if set_portrait:
dialogic.Portraits.change_character_portrait(character, portrait, fade_animation, fade_length)
if set_mirrored:
dialogic.Portraits.change_character_mirror(character, mirrored)
if set_z_index:
dialogic.Portraits.change_character_z_index(character, z_index)
if set_transform:
dialogic.Portraits.move_character(character, transform, final_position_move_time, transform_ease, transform_trans)
if animation_name:
var final_animation_repetitions: int = animation_repeats
if dialogic.Inputs.auto_skip.enabled:
var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event
var time_for_repetitions: float = time_per_event / animation_repeats
final_animation_length = time_for_repetitions
var animation := dialogic.Portraits.animate_character(
character,
animation_name,
final_animation_length,
final_animation_repetitions,
)
if animation_wait:
dialogic.current_state = DialogicGameHandler.States.ANIMATING
await animation.finished
dialogic.current_state = DialogicGameHandler.States.IDLE
finish()
#region INITIALIZE
###############################################################################
func _init() -> void:
event_name = "Character"
set_default_color('Color2')
event_category = "Main"
event_sorting_index = 2
func _get_icon() -> Resource:
return load(self.get_script().get_path().get_base_dir().path_join('icon.svg'))
#endregion
#region SAVING, LOADING, DEFAULTS
################################################################################
func to_text() -> String:
var result_string := ""
# ACTIONS
match action:
Actions.JOIN: result_string += "join "
Actions.LEAVE: result_string += "leave "
Actions.UPDATE: result_string += "update "
var default_values := DialogicUtil.get_custom_event_defaults(event_name)
# CHARACTER IDENTIFIER
if action == Actions.LEAVE and character_identifier == '--All--':
result_string += "--All--"
elif character:
var name := DialogicResourceUtil.get_unique_identifier(character.resource_path)
if name.count(" ") > 0:
name = '"' + name + '"'
result_string += name
# PORTRAIT
if portrait.strip_edges() != default_values.get('portrait', ''):
if action != Actions.LEAVE and (action != Actions.UPDATE or set_portrait):
result_string += " (" + portrait + ")"
# TRANSFORM
if action == Actions.JOIN or (action == Actions.UPDATE and set_transform):
result_string += " " + str(transform)
# SETS:
if action == Actions.JOIN or action == Actions.LEAVE:
set_mirrored = mirrored != default_values.get("mirrored", false)
set_z_index = z_index != default_values.get("z_index", 0)
var shortcode := store_to_shortcode_parameters()
if shortcode != "":
result_string += " [" + shortcode + "]"
return result_string
func from_text(string:String) -> void:
# Load default character
character = DialogicResourceUtil.get_character_resource(character_identifier)
var result := regex.search(string)
# ACTION
match result.get_string('type'):
"join": action = Actions.JOIN
"leave": action = Actions.LEAVE
"update": action = Actions.UPDATE
# CHARACTER
var given_name := result.get_string('name').strip_edges()
var given_portrait := result.get_string('portrait').strip_edges()
var given_transform := result.get_string('transform').strip_edges()
if given_name:
if action == Actions.LEAVE and given_name == "--All--":
character_identifier = '--All--'
else:
character = DialogicResourceUtil.get_character_resource(given_name)
# PORTRAIT
if given_portrait:
portrait = given_portrait.trim_prefix('(').trim_suffix(')')
set_portrait = true
# TRANSFORM
if given_transform:
transform = given_transform
set_transform = true
# SHORTCODE
if not result.get_string('shortcode'):
return
load_from_shortcode_parameters(result.get_string('shortcode'))
func get_shortcode_parameters() -> Dictionary:
return {
#param_name : property_info
"action" : {"property": "action", "default": 0, "custom_stored":true,
"suggestions": func(): return {'Join':
{'value':Actions.JOIN},
'Leave':{'value':Actions.LEAVE},
'Update':{'value':Actions.UPDATE}}},
"character" : {"property": "character_identifier", "default": "", "custom_stored":true,},
"portrait" : {"property": "portrait", "default": "", "custom_stored":true,},
"transform" : {"property": "transform", "default": "center", "custom_stored":true,},
"animation" : {"property": "animation_name", "default": ""},
"length" : {"property": "animation_length", "default": 0.5},
"wait" : {"property": "animation_wait", "default": false},
"repeat" : {"property": "animation_repeats", "default": 1},
"z_index" : {"property": "z_index", "default": 0},
"mirrored" : {"property": "mirrored", "default": false},
"fade" : {"property": "fade_animation", "default":""},
"fade_length" : {"property": "fade_length", "default":0.5},
"move_time" : {"property": "transform_time", "default": 0.0},
"move_ease" : {"property": "transform_ease", "default": Tween.EaseType.EASE_IN_OUT,
"suggestions": func(): return list_to_suggestions(ease_options)},
"move_trans" : {"property": "transform_trans", "default": Tween.TransitionType.TRANS_SINE,
"suggestions": func(): return list_to_suggestions(trans_options)},
"extra_data" : {"property": "extra_data", "default": ""},
}
func is_valid_event(string:String) -> bool:
if string.begins_with("join") or string.begins_with("leave") or string.begins_with("update"):
return true
return false
#endregion
################################################################################
## EDITOR REPRESENTATION
################################################################################
func build_event_editor() -> void:
add_header_edit('action', ValueType.FIXED_OPTIONS, {
'options': [
{
'label': 'Join',
'value': Actions.JOIN,
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/join.svg")
},
{
'label': 'Leave',
'value': Actions.LEAVE,
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/leave.svg")
},
{
'label': 'Update',
'value': Actions.UPDATE,
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/update.svg")
}
]
})
add_header_edit('character_identifier', ValueType.DYNAMIC_OPTIONS,
{'placeholder' : 'Character',
'file_extension' : '.dch',
'mode' : 2,
'suggestions_func' : get_character_suggestions,
'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg"),
'autofocus' : true})
add_header_edit('set_portrait', ValueType.BOOL_BUTTON,
{'icon':load("res://addons/dialogic/Modules/Character/update_portrait.svg"),
'tooltip':'Change Portrait'}, "should_show_portrait_selector() and action == Actions.UPDATE")
add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS,
{'placeholder' : 'Default',
'collapse_when_empty':true,
'suggestions_func' : get_portrait_suggestions,
'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")},
'should_show_portrait_selector() and (action != Actions.UPDATE or set_portrait)')
add_header_edit('set_transform', ValueType.BOOL_BUTTON,
{'icon': load("res://addons/dialogic/Modules/Character/update_position.svg"), 'tooltip':'Change Position'}, "character != null and !has_no_portraits() and action == Actions.UPDATE")
add_header_label('at position', 'character != null and !has_no_portraits() and action == Actions.JOIN')
add_header_label('to position', 'character != null and !has_no_portraits() and action == Actions.UPDATE and set_transform')
add_header_edit('transform', ValueType.DYNAMIC_OPTIONS,
{'placeholder' : 'center',
'mode' : 0,
'suggestions_func' : get_position_suggestions,
'tooltip' : "You can use a predefined position or a custom transform like 'pos=x0.5y1 size=x0.5y1 rot=10'.\nLearn more about this in the documentation."},
'character != null and !has_no_portraits() and action != %s and (action != Actions.UPDATE or set_transform)' %Actions.LEAVE)
# Body
add_body_edit('fade_animation', ValueType.DYNAMIC_OPTIONS,
{'left_text' : 'Fade:',
'suggestions_func' : get_fade_suggestions,
'editor_icon' : ["Animation", "EditorIcons"],
'placeholder' : 'Default',
'enable_pretty_name' : true},
'should_show_fade_options()')
add_body_edit('fade_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s', "min":0},
'should_show_fade_options() and !fade_animation.is_empty()')
add_body_line_break("should_show_fade_options()")
add_body_edit('animation_name', ValueType.DYNAMIC_OPTIONS,
{'left_text' : 'Animation:',
'suggestions_func' : get_animation_suggestions,
'editor_icon' : ["Animation", "EditorIcons"],
'placeholder' : 'Default',
'enable_pretty_name' : true},
'should_show_animation_options()')
add_body_edit('animation_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s', "min":0},
'should_show_animation_options() and !animation_name.is_empty()')
add_body_edit('animation_wait', ValueType.BOOL, {'left_text':'Await end:'},
'should_show_animation_options() and !animation_name.is_empty()')
add_body_edit('animation_repeats', ValueType.NUMBER, {'left_text':'Repeat:', 'mode':1, "min":1},
'should_show_animation_options() and !animation_name.is_empty() and action == %s)' %Actions.UPDATE)
add_body_line_break()
add_body_edit('transform_time', ValueType.NUMBER, {'left_text':'Movement duration:', "min":0},
"should_show_transform_options()")
add_body_edit("transform_trans", ValueType.FIXED_OPTIONS, {'options':trans_options, 'left_text':"Trans:"}, 'should_show_transform_options() and transform_time > 0')
add_body_edit("transform_ease", ValueType.FIXED_OPTIONS, {'options':ease_options, 'left_text':"Ease:"}, 'should_show_transform_options() and transform_time > 0')
add_body_edit('set_z_index', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_z_index.svg"), 'tooltip':'Change Z-Index'}, "character != null and action == Actions.UPDATE")
add_body_edit('z_index', ValueType.NUMBER, {'left_text':'Z-index:', 'mode':1},
'action != %s and (action != Actions.UPDATE or set_z_index)' %Actions.LEAVE)
add_body_edit('set_mirrored', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_mirror.svg"), 'tooltip':'Change Mirroring'}, "character != null and action == Actions.UPDATE")
add_body_edit('mirrored', ValueType.BOOL, {'left_text':'Mirrored:'},
'action != %s and (action != Actions.UPDATE or set_mirrored)' %Actions.LEAVE)
add_body_edit('extra_data', ValueType.SINGLELINE_TEXT, {'left_text':'Extra Data:'}, 'action != Actions.LEAVE')
func should_show_transform_options() -> bool:
return action == Actions.UPDATE and set_transform
func should_show_animation_options() -> bool:
return (character and !character.portraits.is_empty()) or character_identifier == '--All--'
func should_show_fade_options() -> bool:
return action == Actions.UPDATE and set_portrait and character and not character.portraits.is_empty()
func should_show_portrait_selector() -> bool:
return character and len(character.portraits) > 1 and action != Actions.LEAVE
func has_no_portraits() -> bool:
return character and character.portraits.is_empty()
func get_character_suggestions(search_text:String) -> Dictionary:
return DialogicUtil.get_character_suggestions(search_text, character, false, action == Actions.LEAVE, editor_node)
func get_portrait_suggestions(search_text:String) -> Dictionary:
var empty_text := "Don't Change"
if action == Actions.JOIN:
empty_text = "Default portrait"
return DialogicUtil.get_portrait_suggestions(search_text, character, true, empty_text)
func get_position_suggestions(search_text:String='') -> Dictionary:
return DialogicUtil.get_portrait_position_suggestions(search_text)
func get_animation_suggestions(search_text:String='') -> Dictionary:
var DPAU := DialogicPortraitAnimationUtil
match action:
Actions.JOIN:
return DPAU.get_suggestions(search_text, animation_name, "Default", DPAU.AnimationType.IN)
Actions.LEAVE:
return DPAU.get_suggestions(search_text, animation_name, "Default", DPAU.AnimationType.OUT)
Actions.UPDATE:
return DPAU.get_suggestions(search_text, animation_name, "None", DPAU.AnimationType.ACTION)
return {}
func get_fade_suggestions(search_text:String='') -> Dictionary:
return DialogicPortraitAnimationUtil.get_suggestions(search_text, fade_animation, "Default", DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)
####################### CODE COMPLETION ########################################
################################################################################
func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void:
var line_until_caret: String = CodeCompletionHelper.get_line_untill_caret(line)
if symbol == ' ' and line_until_caret.count(' ') == 1:
CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_MEMBER)
if line.begins_with('leave'):
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'All', '--All-- ', event_color, TextNode.get_theme_icon("GuiEllipsis", "EditorIcons"))
if symbol == '(':
var completion_character := regex.search(line).get_string('name')
CodeCompletionHelper.suggest_portraits(TextNode, completion_character)
elif not '[' in line_until_caret and symbol == ' ':
if not line.begins_with("leave"):
for position in get_position_suggestions():
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, position, position+' ', TextNode.syntax_highlighter.normal_color)
# Shortcode Part
if '[' in line_until_caret:
# Suggest Parameters
if symbol == '[' or symbol == ' ' and line_until_caret.count('"')%2 == 0:# and (symbol == "[" or (symbol == " " and line_until_caret.rfind('="') < line_until_caret.rfind('"')-1)):
suggest_parameter("animation", line, TextNode)
if "animation=" in line:
for param in ["length", "wait"]:
suggest_parameter(param, line, TextNode)
if line.begins_with('update'):
suggest_parameter("repeat", line, TextNode)
if line.begins_with("update"):
for param in ["move_time", "move_trans", "move_ease"]:
suggest_parameter(param, line, TextNode)
if not line.begins_with('leave'):
for param in ["mirrored", "z_index", "extra_data"]:
suggest_parameter(param, line, TextNode)
# Suggest Values
else:
var current_param: RegExMatch = CodeCompletionHelper.completion_shortcode_param_getter_regex.search(line)
if not current_param:
return
match current_param.get_string("param"):
"animation":
var animations := {}
if line.begins_with('join'):
animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.IN)
elif line.begins_with('update'):
animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.ACTION)
elif line.begins_with('leave'):
animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.OUT)
for script: String in animations:
TextNode.add_code_completion_option(CodeEdit.KIND_VARIABLE, DialogicUtil.pretty_name(script), DialogicUtil.pretty_name(script), TextNode.syntax_highlighter.normal_color, null, '" ')
"wait", "mirrored":
CodeCompletionHelper.suggest_bool(TextNode, TextNode.syntax_highlighter.normal_color)
"move_trans":
CodeCompletionHelper.suggest_custom_suggestions(list_to_suggestions(trans_options), TextNode, TextNode.syntax_highlighter.normal_color)
"move_ease":
CodeCompletionHelper.suggest_custom_suggestions(list_to_suggestions(ease_options), TextNode, TextNode.syntax_highlighter.normal_color)
func suggest_parameter(parameter:String, line:String, TextNode:TextEdit) -> void:
if not parameter + "=" in line:
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, parameter, parameter + '="', TextNode.syntax_highlighter.normal_color)
func _get_start_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'join', 'join ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/join.svg'))
TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'leave', 'leave ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/leave.svg'))
TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'update', 'update ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/update.svg'))
#################### SYNTAX HIGHLIGHTING #######################################
################################################################################
func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary:
var word := line.get_slice(' ', 0)
dict[line.find(word)] = {"color":event_color}
dict[line.find(word)+len(word)] = {"color":Highlighter.normal_color}
var result := regex.search(line)
if result.get_string('name'):
dict[result.get_start('name')] = {"color":event_color.lerp(Highlighter.normal_color, 0.5)}
dict[result.get_end('name')] = {"color":Highlighter.normal_color}
if result.get_string('portrait'):
dict[result.get_start('portrait')] = {"color":event_color.lerp(Highlighter.normal_color, 0.6)}
dict[result.get_end('portrait')] = {"color":Highlighter.normal_color}
if result.get_string('shortcode'):
dict = Highlighter.color_shortcode_content(dict, line, result.get_start('shortcode'), result.get_end('shortcode'), event_color)
return dict
## HELPER
func list_to_suggestions(list:Array) -> Dictionary:
return list.reduce(
func(accum, value):
accum[value.label] = value
accum[value.label]["text_alt"] = [value.label.to_lower()]
return accum,
{})

View File

@@ -0,0 +1 @@
uid://xxqa000yatnt

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dn5jx2ucynfio"
path="res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Events/Character/icon.png"
dest_files=["res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.0625 10.483C21.0625 13.5111 18.6077 15.9659 15.5795 15.9659C12.5514 15.9659 10.0966 13.5111 10.0966 10.483C10.0966 7.4548 12.5514 5 15.5795 5C18.6077 5 21.0625 7.4548 21.0625 10.483Z" fill="white"/>
<path d="M22.3158 24.1393C22.3158 27 19.3349 27 15.6579 27C11.9808 27 9 27 9 24.1393C9 19.0046 11.9808 14.8421 15.6579 14.8421C19.3349 14.8421 22.3158 19.0046 22.3158 24.1393Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://q6lanmf18ii6"
path="res://.godot/imported/icon.svg-4f340f6efbb83004dbd5c761dd1dc448.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/icon.svg"
dest_files=["res://.godot/imported/icon.svg-4f340f6efbb83004dbd5c761dd1dc448.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cu6cj55m6dr78"
path="res://.godot/imported/icon_position.png-221b7c348ec45b22fd4df49fdb92a7aa.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/icon_position.png"
dest_files=["res://.godot/imported/icon_position.png-221b7c348ec45b22fd4df49fdb92a7aa.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,58 @@
@tool
extends DialogicIndexer
func _get_events() -> Array:
return [this_folder.path_join('event_character.gd')]
func _get_subsystems() -> Array:
return [{'name':'Portraits', 'script':this_folder.path_join('subsystem_portraits.gd')}, {'name':'PortraitContainers', 'script':this_folder.path_join('subsystem_containers.gd')}]
func _get_settings_pages() -> Array:
return [this_folder.path_join('settings_portraits.tscn')]
func _get_text_effects() -> Array[Dictionary]:
return [
{'command':'portrait', 'subsystem':'Portraits', 'method':'text_effect_portrait', 'arg':true},
{'command':'extra_data', 'subsystem':'Portraits', 'method':'text_effect_extradata', 'arg':true},
]
func _get_special_resources() -> Dictionary:
return {&'PortraitAnimation': list_animations("DefaultAnimations")}
func _get_portrait_scene_presets() -> Array[Dictionary]:
return [
{
"path": "",
"name": "Default Scene",
"description": "The default scene defined in Settings>Portraits.",
"author":"Dialogic",
"type": "Default",
"icon":"",
"preview_image":[this_folder.path_join("default_portrait_thumbnail.png")],
"documentation":"",
},
{
"path": "CUSTOM",
"name": "Custom Scene",
"description": "A custom scene. Should extend DialogicPortrait and be in @tool mode.",
"author":"Dialogic",
"type": "Custom",
"icon":"",
"preview_image":[this_folder.path_join("custom_portrait_thumbnail.png")],
"documentation":"https://docs.dialogic.pro/custom-portraits.html",
},
{
"path": this_folder.path_join("default_portrait.tscn"),
"name": "Simple Image Portrait",
"description": "Can display images as portraits. Does nothing else.",
"author":"Dialogic",
"type": "General",
"icon":"",
"preview_image":[this_folder.path_join("simple_image_portrait_thumbnail.png")],
"documentation":"",
}
]

View File

@@ -0,0 +1 @@
uid://drr427b3tvasg

View File

@@ -0,0 +1,258 @@
@tool
class_name DialogicNode_PortraitContainer
extends Control
## Node that defines a position for dialogic portraits and how to display portraits at that position.
enum PositionModes {
POSITION, ## This container can be joined/moved to with the Character Event
SPEAKER, ## This container is joined/left automatically based on the speaker.
}
@export var mode := PositionModes.POSITION
@export_subgroup('Mode: Position')
## The position this node corresponds to.
@export var container_ids: PackedStringArray = ["1"]
@export_subgroup('Mode: Speaker')
## Can be used to use a different portrait.
## E.g. "Faces/" would mean instead of "happy" it will use portrait "Faces/happy"
@export var portrait_prefix := ''
@export_subgroup('Portrait Placement')
enum SizeModes {
KEEP, ## The height and width of the container have no effect, only the origin.
FIT_STRETCH, ## The portrait will be fitted into the container, ignoring it's aspect ratio and the character/portrait scale.
FIT_IGNORE_SCALE, ## The portrait will be fitted into the container, ignoring the character/portrait scale, but preserving the aspect ratio.
FIT_SCALE_HEIGHT ## Recommended. The portrait will be scaled to fit the container height. A character/portrait scale of 100% means 100% container height. Aspect ratio will be preserved.
}
## Defines how to affect the scale of the portrait
@export var size_mode: SizeModes = SizeModes.FIT_SCALE_HEIGHT :
set(mode):
size_mode = mode
_update_debug_portrait_transform()
## If true, portraits will be mirrored in this position.
@export var mirrored := false :
set(mirror):
mirrored = mirror
_update_debug_portrait_scene()
@export_group('Origin', 'origin')
enum OriginAnchors {TOP_LEFT, TOP_CENTER, TOP_RIGHT, LEFT_MIDDLE, CENTER, RIGHT_MIDDLE, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT}
## The portrait will be placed relative to this point in the container.
@export var origin_anchor: OriginAnchors = OriginAnchors.BOTTOM_CENTER :
set(anchor):
origin_anchor = anchor
_update_debug_origin()
## An offset to apply to the origin. Rarely useful.
@export var origin_offset := Vector2() :
set(offset):
origin_offset = offset
_update_debug_origin()
enum PivotModes {AT_ORIGIN, PERCENTAGE, PIXELS}
## Usually you want to rotate or scale around the portrait origin.
## For the moments where that is not the case, set the mode to PERCENTAGE or PIXELS and use [member pivot_value].
@export var pivot_mode: PivotModes = PivotModes.AT_ORIGIN
## Only has an effect when [member pivot_mode] is not AT_ORIGIN. Meaning depends on whether [member pivot_mode] is PERCENTAGE or PIXELS.
@export var pivot_value := Vector2()
@export_group('Debug', 'debug')
## A character that will be displayed in the editor, useful for getting the right size.
@export var debug_character: DialogicCharacter = null:
set(character):
debug_character = character
_update_debug_portrait_scene()
@export var debug_character_portrait := "":
set(portrait):
debug_character_portrait = portrait
_update_debug_portrait_scene()
var debug_character_holder_node: Node2D = null
var debug_character_scene_node: Node = null
var debug_origin: Sprite2D = null
var default_portrait_scene: String = DialogicUtil.get_module_path('Character').path_join("default_portrait.tscn")
# Used if no debug character is specified
var default_debug_character := load(DialogicUtil.get_module_path('Character').path_join("preview_character.tres"))
var ignore_resize := false
func _ready() -> void:
match mode:
PositionModes.POSITION:
add_to_group('dialogic_portrait_con_position')
PositionModes.SPEAKER:
add_to_group('dialogic_portrait_con_speaker')
if Engine.is_editor_hint():
resized.connect(_update_debug_origin)
if !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty():
default_portrait_scene = ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')
debug_origin = Sprite2D.new()
add_child(debug_origin)
debug_origin.texture = load("res://addons/dialogic/Editor/Images/Dropdown/default.svg")
_update_debug_origin()
_update_debug_portrait_scene()
else:
resized.connect(update_portrait_transforms)
################################################################################
## MAIN METHODS
################################################################################
func update_portrait_transforms() -> void:
if ignore_resize:
return
match pivot_mode:
PivotModes.AT_ORIGIN:
pivot_offset = _get_origin_position()
PivotModes.PERCENTAGE:
pivot_offset = size*pivot_value
PivotModes.PIXELS:
pivot_offset = pivot_value
for child in get_children():
DialogicUtil.autoload().Portraits._update_character_transform(child)
## Returns a Rect2 with the position as the position and the scale as the size.
func get_local_portrait_transform(portrait_rect:Rect2, character_scale:=1.0) -> Rect2:
var transform := Rect2()
transform.position = _get_origin_position()
# Mode that ignores the containers size
if size_mode == SizeModes.KEEP:
transform.size = Vector2(1,1) * character_scale
# Mode that makes sure neither height nor width go out of container
elif size_mode == SizeModes.FIT_IGNORE_SCALE:
if size.x/size.y < portrait_rect.size.x/portrait_rect.size.y:
transform.size = Vector2(1,1) * size.x/portrait_rect.size.x
else:
transform.size = Vector2(1,1) * size.y/portrait_rect.size.y
# Mode that stretches the portrait to fill the whole container
elif size_mode == SizeModes.FIT_STRETCH:
transform.size = size/portrait_rect.size
# Mode that size the character so 100% size fills the height
elif size_mode == SizeModes.FIT_SCALE_HEIGHT:
transform.size = Vector2(1,1) * size.y / portrait_rect.size.y*character_scale
return transform
## Returns the current origin position
func _get_origin_position(rect_size = null) -> Vector2:
if rect_size == null:
rect_size = size
return rect_size * Vector2(origin_anchor%3 / 2.0, floor(origin_anchor/3.0) / 2.0) + origin_offset
func is_container(id:Variant) -> bool:
return str(id) in container_ids
#region DEBUG METHODS
################################################################################
### USE THIS TO DEBUG THE POSITIONS
#func _draw():
#draw_rect(Rect2(Vector2(), size), Color(1, 0.3098039329052, 1), false, 2)
#draw_string(get_theme_default_font(),get_theme_default_font().get_string_size(container_ids[0], HORIZONTAL_ALIGNMENT_LEFT, 1, get_theme_default_font_size()) , container_ids[0], HORIZONTAL_ALIGNMENT_CENTER)
#
#func _process(delta:float) -> void:
#queue_redraw()
## Loads the debug_character with the debug_character_portrait
## Creates a holder node and applies mirror
func _update_debug_portrait_scene() -> void:
if !Engine.is_editor_hint():
return
if is_instance_valid(debug_character_holder_node):
for child in get_children():
if child != debug_origin:
child.free()
# Get character
var character := _get_debug_character()
if not character is DialogicCharacter or character.portraits.is_empty():
return
# Determine portrait
var debug_portrait := debug_character_portrait
if debug_portrait.is_empty():
debug_portrait = character.default_portrait
if mode == PositionModes.SPEAKER and !portrait_prefix.is_empty():
if portrait_prefix+debug_portrait in character.portraits:
debug_portrait = portrait_prefix+debug_portrait
if not debug_portrait in character.portraits:
debug_portrait = character.default_portrait
var portrait_info: Dictionary = character.get_portrait_info(debug_portrait)
# Determine scene
var portrait_scene_path: String = portrait_info.get('scene', default_portrait_scene)
if portrait_scene_path.is_empty():
portrait_scene_path = default_portrait_scene
debug_character_scene_node = load(portrait_scene_path).instantiate()
if !is_instance_valid(debug_character_scene_node):
return
# Load portrait
DialogicUtil.apply_scene_export_overrides(debug_character_scene_node, character.portraits[debug_portrait].get('export_overrides', {}))
debug_character_scene_node._update_portrait(character, debug_portrait)
# Add character node
if !is_instance_valid(debug_character_holder_node):
debug_character_holder_node = Node2D.new()
add_child(debug_character_holder_node)
# Add portrait node
debug_character_holder_node.add_child(debug_character_scene_node)
move_child(debug_character_holder_node, 0)
debug_character_scene_node._set_mirror(character.mirror != mirrored != portrait_info.get('mirror', false))
_update_debug_portrait_transform()
## Set's the size and position of the holder and scene node
## according to the size_mode
func _update_debug_portrait_transform() -> void:
if !Engine.is_editor_hint() or !is_instance_valid(debug_character_scene_node) or !is_instance_valid(debug_origin):
return
var character := _get_debug_character()
var portrait_info := character.get_portrait_info(debug_character_portrait)
var transform := get_local_portrait_transform(debug_character_scene_node._get_covered_rect(), character.scale*portrait_info.get('scale', 1))
debug_character_holder_node.position = transform.position
debug_character_scene_node.position = portrait_info.get('offset', Vector2())+character.offset
debug_character_holder_node.scale = transform.size
## Updates the debug origins position. Also calls _update_debug_portrait_transform()
func _update_debug_origin() -> void:
if !Engine.is_editor_hint() or !is_instance_valid(debug_origin):
return
debug_origin.position = _get_origin_position()
_update_debug_portrait_transform()
## Returns the debug character or the default debug character
func _get_debug_character() -> DialogicCharacter:
return debug_character if debug_character != null else default_debug_character
#endregion

View File

@@ -0,0 +1 @@
uid://ci5bin54hnssd

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" version="1.1" id="svg13818" sodipodi:docname="event_portrait_position.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs13822" />
<sodipodi:namedview id="namedview13820" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="false" inkscape:zoom="6.5390625" inkscape:cx="-20.109916" inkscape:cy="11.622461" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg13818" />
<path d="m 41.97648,22.585683 c 0,5.509774 -4.466676,9.976311 -9.976424,9.976311 -5.509775,0 -9.976338,-4.466537 -9.976338,-9.976311 0,-5.509775 4.466563,-9.976328 9.976338,-9.976328 5.509748,0 9.976424,4.466553 9.976424,9.976328 z" fill="#ffffff" id="path13814" style="stroke-width:2.74349" />
<path d="m 43.971538,47.35052 c 0,5.164069 -5.359673,5.164069 -11.971482,5.164069 -6.611724,0 -11.971595,0 -11.971595,-5.164069 0,-9.269422 5.359871,-16.783783 11.971595,-16.783783 6.611808,0 11.971482,7.514361 11.971482,16.783783 z" fill="#ffffff" id="path13816" style="stroke-width:2.74349" />
<rect style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4.081;stroke-linecap:round;stroke-miterlimit:3.2;stroke-dasharray:4.081,8.162;stroke-dashoffset:0" id="rect14304" width="35.873577" height="49.97176" x="14.06331" y="6.9472136" ry="0.19193064" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bn3nq7gw67kye"
path="res://.godot/imported/portrait_position.svg-f025767e40032b9ebdeab1f9e882467a.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/portrait_position.svg"
dest_files=["res://.godot/imported/portrait_position.svg-f025767e40032b9ebdeab1f9e882467a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,32 @@
[gd_resource type="Resource" script_class="DialogicCharacter" load_steps=2 format=3 uid="uid://dykf1j17ct5mo"]
[ext_resource type="Script" path="res://addons/dialogic/Resources/character.gd" id="1_qsljv"]
[resource]
script = ExtResource("1_qsljv")
display_name = "Preview"
nicknames = [""]
color = Color(1, 1, 1, 1)
description = ""
scale = 1.0
offset = Vector2(0, 0)
mirror = false
default_portrait = "character"
portraits = {
"character": {
"image": "res://addons/dialogic/Editor/Images/preview_character.png",
"offset": Vector2(0, 0),
"scene": "res://addons/dialogic/Modules/Character/default_portrait.tscn"
},
"speaker": {
"image": "res://addons/dialogic/Editor/Images/preview_character_speaker.png",
"offset": Vector2(0, 0),
"scene": "res://addons/dialogic/Modules/Character/default_portrait.tscn"
}
}
custom_info = {
"sound_mood_default": "",
"sound_moods": {},
"style": ""
}
metadata/timeline_not_saved = true

View File

@@ -0,0 +1,92 @@
@tool
extends DialogicSettingsPage
const POSITION_SUGGESTION_KEY := 'dialogic/portraits/position_suggestion_names'
const DEFAULT_PORTRAIT_SCENE_KEY := 'dialogic/portraits/default_portrait'
const ANIMATION_JOIN_DEFAULT_KEY := 'dialogic/animations/join_default'
const ANIMATION_JOIN_DEFAULT_LENGTH_KEY := 'dialogic/animations/join_default_length'
const ANIMATION_JOIN_DEFAULT_WAIT_KEY := 'dialogic/animations/join_default_wait'
const ANIMATION_LEAVE_DEFAULT_KEY := 'dialogic/animations/leave_default'
const ANIMATION_LEAVE_DEFAULT_LENGTH_KEY := 'dialogic/animations/leave_default_length'
const ANIMATION_LEAVE_DEFAULT_WAIT_KEY := 'dialogic/animations/leave_default_wait'
const ANIMATION_CROSSFADE_DEFAULT_KEY := 'dialogic/animations/cross_fade_default'
const ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY:= 'dialogic/animations/cross_fade_default_length'
func _ready():
%JoinDefault.get_suggestions_func = get_join_animation_suggestions
%JoinDefault.mode = 1
%LeaveDefault.get_suggestions_func = get_leave_animation_suggestions
%LeaveDefault.mode = 1
%CrossFadeDefault.get_suggestions_func = get_crossfade_animation_suggestions
%CrossFadeDefault.mode = 1
%PositionSuggestions.text_submitted.connect(save_setting.bind(POSITION_SUGGESTION_KEY))
%CustomPortraitScene.value_changed.connect(save_setting_with_name.bind(DEFAULT_PORTRAIT_SCENE_KEY))
%JoinDefault.value_changed.connect(
save_setting_with_name.bind(ANIMATION_JOIN_DEFAULT_KEY))
%JoinDefaultLength.value_changed.connect(
save_setting.bind(ANIMATION_JOIN_DEFAULT_LENGTH_KEY))
%JoinDefaultWait.toggled.connect(
save_setting.bind(ANIMATION_JOIN_DEFAULT_WAIT_KEY))
%LeaveDefault.value_changed.connect(
save_setting_with_name.bind(ANIMATION_LEAVE_DEFAULT_KEY))
%LeaveDefaultLength.value_changed.connect(
save_setting.bind(ANIMATION_LEAVE_DEFAULT_LENGTH_KEY))
%LeaveDefaultWait.toggled.connect(
save_setting.bind(ANIMATION_LEAVE_DEFAULT_WAIT_KEY))
%CrossFadeDefault.value_changed.connect(
save_setting_with_name.bind(ANIMATION_CROSSFADE_DEFAULT_KEY))
%CrossFadeDefaultLength.value_changed.connect(
save_setting.bind(ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY))
func _refresh():
%PositionSuggestions.text = ProjectSettings.get_setting(POSITION_SUGGESTION_KEY, 'leftmost, left, center, right, rightmost')
%CustomPortraitScene.resource_icon = get_theme_icon(&"PackedScene", &"EditorIcons")
%CustomPortraitScene.set_value(ProjectSettings.get_setting(DEFAULT_PORTRAIT_SCENE_KEY, ''))
# JOIN
%JoinDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons")
%JoinDefault.set_value(ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_KEY, "Fade In Up"))
%JoinDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_LENGTH_KEY, 0.5))
%JoinDefaultWait.button_pressed = ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_WAIT_KEY, true)
# LEAVE
%LeaveDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons")
%LeaveDefault.set_value(ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_KEY, "Fade Out Down"))
%LeaveDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_LENGTH_KEY, 0.5))
%LeaveDefaultWait.button_pressed = ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_WAIT_KEY, true)
# CROSS FADE
%CrossFadeDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons")
%CrossFadeDefault.set_value(ProjectSettings.get_setting(ANIMATION_CROSSFADE_DEFAULT_KEY, "Fade Cross"))
%CrossFadeDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY, 0.5))
func save_setting_with_name(property_name:String, value:Variant, settings_key:String) -> void:
save_setting(value, settings_key)
func save_setting(value:Variant, settings_key:String) -> void:
ProjectSettings.set_setting(settings_key, value)
ProjectSettings.save()
func get_join_animation_suggestions(search_text:String) -> Dictionary:
return DialogicPortraitAnimationUtil.get_suggestions(search_text, %JoinDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.IN)
func get_leave_animation_suggestions(search_text:String) -> Dictionary:
return DialogicPortraitAnimationUtil.get_suggestions(search_text, %LeaveDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.OUT)
func get_crossfade_animation_suggestions(search_text:String) -> Dictionary:
return DialogicPortraitAnimationUtil.get_suggestions(search_text, %CrossFadeDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)

View File

@@ -0,0 +1 @@
uid://d0gwrct1glpjy

View File

@@ -0,0 +1,160 @@
[gd_scene load_steps=5 format=3 uid="uid://cp463rpri5j8a"]
[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/settings_portraits.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_dce78"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3"]
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="3_m06d8"]
[node name="Portraits" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource("2")
[node name="PositionsTitle" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.5
[node name="Title2" type="Label" parent="PositionsTitle"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Position Suggestions
"
[node name="HintTooltip" parent="PositionsTitle" instance=ExtResource("2_dce78")]
layout_mode = 2
tooltip_text = "You can change the position names that will be suggested in the timeline editor here."
texture = null
hint_text = "You can change the position names that will be suggested in the timeline editor here."
[node name="PositionSuggestions" type="LineEdit" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="DefaultSceneTitle" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.5
[node name="Title2" type="Label" parent="DefaultSceneTitle"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Default Portrait Scene
"
[node name="HintTooltip" parent="DefaultSceneTitle" instance=ExtResource("2_dce78")]
layout_mode = 2
tooltip_text = "If this is set, this scene will be what is used by default for any portrait that has no scene specified"
texture = null
hint_text = "If this is set, this scene will be what is used by default for any portrait that has no scene specified"
[node name="DefaultScene" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="DefaultScene"]
layout_mode = 2
text = "Scene"
[node name="CustomPortraitScene" parent="DefaultScene" instance=ExtResource("3_m06d8")]
unique_name_in_owner = true
layout_mode = 2
file_filter = "*.tscn, *.scn; PortraitScene"
placeholder = "Default Scene"
[node name="Animations2" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.5
[node name="Title2" type="Label" parent="Animations2"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Default Animations
"
[node name="HintTooltip" parent="Animations2" instance=ExtResource("2_dce78")]
layout_mode = 2
tooltip_text = "These settings are used for Leave and Join events if no animation is selected.
The Cross-Fade will play if the portrait of a character changes and
no animation is set."
texture = null
hint_text = "These settings are used for Leave and Join events if no animation is selected.
The Cross-Fade will play if the portrait of a character changes and
no animation is set."
[node name="GridContainer" type="GridContainer" parent="."]
layout_mode = 2
columns = 2
[node name="DefaultJoinLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Join"
[node name="DefaultIn" type="HBoxContainer" parent="GridContainer"]
layout_mode = 2
[node name="JoinDefault" parent="GridContainer/DefaultIn" instance=ExtResource("3")]
unique_name_in_owner = true
layout_mode = 2
mode = 1
[node name="JoinDefaultLength" type="SpinBox" parent="GridContainer/DefaultIn"]
unique_name_in_owner = true
layout_mode = 2
step = 0.1
[node name="JoinDefaultWait" type="CheckButton" parent="GridContainer/DefaultIn"]
unique_name_in_owner = true
layout_mode = 2
text = "Wait:"
[node name="DefaultOutLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Leave"
[node name="DefaultOut" type="HBoxContainer" parent="GridContainer"]
layout_mode = 2
[node name="LeaveDefault" parent="GridContainer/DefaultOut" instance=ExtResource("3")]
unique_name_in_owner = true
layout_mode = 2
mode = 1
[node name="LeaveDefaultLength" type="SpinBox" parent="GridContainer/DefaultOut"]
unique_name_in_owner = true
layout_mode = 2
step = 0.1
[node name="LeaveDefaultWait" type="CheckButton" parent="GridContainer/DefaultOut"]
unique_name_in_owner = true
layout_mode = 2
text = "Wait:"
[node name="CrossFadeLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Cross-Fade"
[node name="DefaultCrossFade" type="HBoxContainer" parent="GridContainer"]
layout_mode = 2
[node name="CrossFadeDefault" parent="GridContainer/DefaultCrossFade" instance=ExtResource("3")]
unique_name_in_owner = true
layout_mode = 2
mode = 1
[node name="CrossFadeDefaultLength" type="SpinBox" parent="GridContainer/DefaultCrossFade"]
unique_name_in_owner = true
layout_mode = 2
step = 0.1
[connection signal="value_changed" from="GridContainer/DefaultIn/JoinDefault" to="." method="_on_JoinDefault_value_changed"]
[connection signal="value_changed" from="GridContainer/DefaultIn/JoinDefaultLength" to="." method="_on_JoinDefaultLength_value_changed"]
[connection signal="toggled" from="GridContainer/DefaultIn/JoinDefaultWait" to="." method="_on_JoinDefaultWait_toggled"]
[connection signal="value_changed" from="GridContainer/DefaultOut/LeaveDefault" to="." method="_on_LeaveDefault_value_changed"]
[connection signal="value_changed" from="GridContainer/DefaultOut/LeaveDefaultLength" to="." method="_on_LeaveDefaultLength_value_changed"]
[connection signal="toggled" from="GridContainer/DefaultOut/LeaveDefaultWait" to="." method="_on_LeaveDefaultWait_toggled"]
[connection signal="value_changed" from="GridContainer/DefaultCrossFade/CrossFadeDefault" to="." method="_on_LeaveDefault_value_changed"]
[connection signal="value_changed" from="GridContainer/DefaultCrossFade/CrossFadeDefaultLength" to="." method="_on_LeaveDefaultLength_value_changed"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://qihj11n7kx3m"
path="res://.godot/imported/simple_image_portrait_thumbnail.png-3d6c6de8187fd7911017e2ef9d3c40a4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png"
dest_files=["res://.godot/imported/simple_image_portrait_thumbnail.png-3d6c6de8187fd7911017e2ef9d3c40a4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,284 @@
extends DialogicSubsystem
## Subsystem that manages portrait positions.
signal position_changed(info: Dictionary)
var transform_regex := r"(?<part>position|pos|size|siz|rotation|rot)\W*=(?<value>((?!(pos|siz|rot)).)*)"
#region STATE
####################################################################################################
#endregion
#region MAIN METHODS
####################################################################################################
func get_container(position_id: String) -> DialogicNode_PortraitContainer:
for portrait_position:DialogicNode_PortraitContainer in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'):
if portrait_position.is_visible_in_tree() and portrait_position.is_container(position_id):
return portrait_position
return null
func get_containers(position_id: String) -> Array[DialogicNode_PortraitContainer]:
return get_tree().get_nodes_in_group(&'dialogic_portrait_con_position').filter(
func(node:DialogicNode_PortraitContainer):
return node.is_visible_in_tree() and node.is_container(position_id))
func get_container_container() -> CanvasItem:
var any_portrait_container := get_tree().get_first_node_in_group(&'dialogic_portrait_con_position')
if any_portrait_container:
return any_portrait_container.get_parent()
return null
## Creates a new portrait container node.
## It will copy it's size and most settings from the first p_container in the tree.
## It will be added as a sibling of the first p_container in the tree.
func add_container(position_id: String) -> DialogicNode_PortraitContainer:
var example_position := get_tree().get_first_node_in_group(&'dialogic_portrait_con_position')
if example_position:
var new_position := DialogicNode_PortraitContainer.new()
example_position.get_parent().add_child(new_position)
new_position.name = "Portrait_"+position_id.validate_node_name()
copy_container_setup(example_position, new_position)
new_position.container_ids = [position_id]
position_changed.emit({&'change':'added', &'container_node':new_position, &'position_id':position_id})
return new_position
return null
## Moves the [container] to the [destionation] (using [tween] and [time]).
## The destination can be a position_id (e.g. "center") or translation, roataion and scale.
## When moving to a preset container, then some more will be "copied" (e.g. anchors, etc.)
func move_container(container:DialogicNode_PortraitContainer, destination:String, tween:Tween = null, time:float=1.0) -> void:
var target_position: Vector2 = container.position + container._get_origin_position()
var target_rotation: float = container.rotation
var target_size: Vector2 = container.size
var destination_container := get_container(destination)
if destination_container:
container.set_meta("target_container", destination_container)
target_position = destination_container.position + destination_container._get_origin_position()
target_rotation = destination_container.rotation_degrees
target_size = destination_container.size
else:
var regex := RegEx.create_from_string(transform_regex)
for found in regex.search_all(destination):
match found.get_string('part'):
'pos', 'position':
target_position = str_to_vector(found.get_string("value"), target_position)
'rot', 'rotation':
target_rotation = float(found.get_string("value"))
'siz', 'size':
target_size = str_to_vector(found.get_string("value"), target_size)
translate_container(container, target_position, false, tween, time)
rotate_container(container, target_rotation, false, tween, time)
resize_container(container, target_size, false, tween, time)
if destination_container:
if time:
tween.finished.connect(func():
if container.has_meta("target_container"):
if container.get_meta("target_container") == destination_container:
copy_container_setup(destination_container, container)
)
else:
copy_container_setup(destination_container, container)
func copy_container_setup(from:DialogicNode_PortraitContainer, to:DialogicNode_PortraitContainer) -> void:
to.ignore_resize = true
to.layout_mode = from.layout_mode
to.anchors_preset = from.anchors_preset
to.anchor_bottom = from.anchor_bottom
to.anchor_left = from.anchor_left
to.anchor_right = from.anchor_right
to.anchor_top = from.anchor_top
to.offset_bottom = from.offset_bottom
to.offset_top = from.offset_top
to.offset_right = from.offset_right
to.offset_left = from.offset_left
to.size_mode = from.size_mode
to.origin_anchor = from.origin_anchor
to.ignore_resize = false
to.update_portrait_transforms()
## Translates the given container.
## The given translation should be the target position of the ORIGIN point, not the container!
func translate_container(container:DialogicNode_PortraitContainer, translation:Variant, relative := false, tween:Tween=null, time:float=1.0) -> void:
if !container.has_meta(&'default_translation'):
container.set_meta(&'default_translation', container.position + container._get_origin_position())
var final_translation: Vector2
if typeof(translation) == TYPE_STRING:
final_translation = str_to_vector(translation, container.position+container._get_origin_position())
elif typeof(translation) == TYPE_VECTOR2:
final_translation = translation
if relative:
final_translation += container.position
else:
final_translation -= container._get_origin_position()
if tween:
tween.tween_method(DialogicUtil.multitween.bind(container, "position", "base"), container.position, final_translation, time)
if not tween.finished.is_connected(save_position_container):
tween.finished.connect(save_position_container.bind(container))
else:
container.position = final_translation
save_position_container(container)
position_changed.emit({&'change':'moved', &'container_node':container})
func rotate_container(container:DialogicNode_PortraitContainer, rotation:float, relative := false, tween:Tween=null, time:float=1.0) -> void:
if !container.has_meta(&'default_rotation'):
container.set_meta(&'default_rotation', container.rotation_degrees)
var final_rotation := rotation
if relative:
final_rotation += container.rotation_degrees
container.pivot_offset = container._get_origin_position()
if tween:
tween.tween_property(container, 'rotation_degrees', final_rotation, time)
if not tween.finished.is_connected(save_position_container):
tween.finished.connect(save_position_container.bind(container))
else:
container.rotation_degrees = final_rotation
save_position_container(container)
position_changed.emit({&'change':'rotated', &'container_node':container})
func resize_container(container: DialogicNode_PortraitContainer, rect_size: Variant, relative := false, tween:Tween=null, time:float=1.0) -> void:
if !container.has_meta(&'default_size'):
container.set_meta(&'default_size', container.size)
var final_rect_resize: Vector2
if typeof(rect_size) == TYPE_STRING:
final_rect_resize = str_to_vector(rect_size, container.size)
elif typeof(rect_size) == TYPE_VECTOR2:
final_rect_resize = rect_size
if relative:
final_rect_resize += container.rect_size
var relative_position_change := container._get_origin_position()-container._get_origin_position(final_rect_resize)
if tween:
tween.tween_method(DialogicUtil.multitween.bind(container, "position", "resize_move"), Vector2(), relative_position_change, time)
tween.tween_property(container, 'size', final_rect_resize, time)
if not tween.finished.is_connected(save_position_container):
tween.finished.connect(save_position_container.bind(container))
else:
container.position = container.position + relative_position_change
container.size = final_rect_resize
save_position_container(container)
position_changed.emit({&'change':'resized', &'container_node':container})
func save_position_container(container: DialogicNode_PortraitContainer) -> void:
if not dialogic.current_state_info.has('portrait_containers'):
dialogic.current_state_info['portrait_containers'] = {}
var info := {
"container_ids" : container.container_ids,
"position" : container.position,
"rotation" : container.rotation_degrees,
"size" : container.size,
"pivot_mode" : container.pivot_mode,
"pivot_value" : container.pivot_value,
"origin_anchor" : container.origin_anchor,
"anchor_left" : container.anchor_left,
"anchor_right" : container.anchor_right,
"anchor_top" : container.anchor_top,
"anchor_bottom" : container.anchor_bottom,
"offset_left" : container.offset_left,
"offset_right" : container.offset_right,
"offset_top" : container.offset_top,
"offset_bottom" : container.offset_bottom,
}
dialogic.current_state_info.portrait_containers[container.container_ids[0]] = info
func load_position_container(position_id: String) -> DialogicNode_PortraitContainer:
# First check whether the container already exists:
var container := get_container(position_id)
if container:
return container
if not dialogic.current_state_info.has('portrait_containers') or not dialogic.current_state_info.portrait_containers.has(position_id):
return null
var info: Dictionary = dialogic.current_state_info.portrait_containers[position_id]
container = add_container(position_id)
if not container:
return null
container.container_ids = info.container_ids
container.position = info.position
container.rotation = info.rotation
container.size = info.size
container.pivot_mode = info.pivot_mode
container.pivot_value = info.pivot_value
container.origin_anchor = info.origin_anchor
container.anchor_left = info.anchor_left
container.anchor_right = info.anchor_right
container.anchor_top = info.anchor_top
container.anchor_bottom = info.anchor_bottom
container.offset_left = info.offset_left
container.offset_right = info.offset_right
container.offset_top = info.offset_top
container.offset_bottom = info.offset_bottom
return container
func str_to_vector(input: String, base_vector:=Vector2()) -> Vector2:
var vector_regex := RegEx.create_from_string(r"(?<part>x|y)\s*(?<number>(-|\+)?(\d|\.|)*)(\s*(?<type>%|px))?")
var vec := base_vector
for i in vector_regex.search_all(input):
var value := float(i.get_string(&'number'))
match i.get_string(&'type'):
'px':
pass # Keep values as they are
'%', _:
match i.get_string(&'part'):
'x': value *= get_viewport().get_visible_rect().size.x
'y': value *= get_viewport().get_visible_rect().size.y
match i.get_string(&'part'):
'x': vec.x = value
'y': vec.y = value
return vec
func vector_to_str(vec:Vector2) -> String:
return "x" + str(vec.x) + "px y" + str(vec.y) + "px"
func reset_all_containers(time:= 0.0, tween:Tween = null) -> void:
for container in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'):
reset_container(container, time, tween)
func reset_container(container:DialogicNode_PortraitContainer, time := 0.0, tween: Tween = null ) -> void:
if container.has_meta(&'default_translation'):
translate_container(container, vector_to_str(container.get_meta(&'default_translation')), false, tween, time)
if container.has_meta(&'default_rotation'):
rotate_container(container, container.get_meta(&'default_rotation'), false, tween, time)
if container.has_meta(&'default_size'):
resize_container(container, vector_to_str(container.get_meta(&'default_size')), false, tween, time)

View File

@@ -0,0 +1 @@
uid://j0oftc7hmr6v

View File

@@ -0,0 +1,714 @@
extends DialogicSubsystem
## Subsystem that manages portraits and portrait positions.
signal character_joined(info:Dictionary)
signal character_left(info:Dictionary)
signal character_portrait_changed(info:Dictionary)
signal character_moved(info:Dictionary)
## Emitted when a portrait starts animating.
#signal portrait_animating(character_node: Node, portrait_node: Node, animation_name: String, animation_length: float)
## The default portrait scene.
var default_portrait_scene: PackedScene = load(get_script().resource_path.get_base_dir().path_join('default_portrait.tscn'))
#region STATE
####################################################################################################
func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
for character in dialogic.current_state_info.get('portraits', {}).keys():
remove_character(load(character))
dialogic.current_state_info['portraits'] = {}
func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void:
if not "portraits" in dialogic.current_state_info:
dialogic.current_state_info["portraits"] = {}
# Load Position Portraits
var portraits_info: Dictionary = dialogic.current_state_info.portraits.duplicate()
dialogic.current_state_info.portraits = {}
for character_path in portraits_info:
var character_info: Dictionary = portraits_info[character_path]
var character: DialogicCharacter = load(character_path)
var container := dialogic.PortraitContainers.load_position_container(character.get_character_name())
add_character(character, container, character_info.portrait, character_info.position_id)
change_character_mirror(character, character_info.get('custom_mirror', false))
change_character_z_index(character, character_info.get('z_index', 0))
change_character_extradata(character, character_info.get('extra_data', ""))
# Load Speaker Portrait
var speaker: Variant = dialogic.current_state_info.get('speaker', "")
if speaker:
dialogic.current_state_info['speaker'] = ""
change_speaker(load(speaker))
dialogic.current_state_info['speaker'] = speaker
func pause() -> void:
for portrait in dialogic.current_state_info['portraits'].values():
if portrait.node.has_meta('animation_node'):
portrait.node.get_meta('animation_node').pause()
func resume() -> void:
for portrait in dialogic.current_state_info['portraits'].values():
if portrait.node.has_meta('animation_node'):
portrait.node.get_meta('animation_node').resume()
func _ready() -> void:
if !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty():
default_portrait_scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', ''))
#region MAIN METHODS
####################################################################################################
## The following methods allow manipulating portraits.
## A portrait is made up of a character node [Node2D] that instances the portrait scene as it's child.
## The character node is then always the child of a portrait container.
## - Position (PortraitContainer)
## ---- character_node (Node2D)
## --------- portrait_node (e.g. default_portrait.tscn, or a custom portrait)
##
## Using these main methods a character can be present multiple times.
## For a VN style, the "character" methods (next section) provide access based on the character.
## - (That is what the character event uses)
## Creates a new [character node] for the given [character], and add it to the given [portrait container].
func _create_character_node(character:DialogicCharacter, container:DialogicNode_PortraitContainer) -> Node:
if container == null:
return null
var character_node := Node2D.new()
character_node.name = character.get_character_name()
character_node.set_meta('character', character)
container.add_child(character_node)
return character_node
## Changes the portrait of a specific [character node].
func _change_portrait(character_node: Node2D, portrait: String, fade_animation:="", fade_length := 0.5) -> Dictionary:
var character: DialogicCharacter = character_node.get_meta('character')
if portrait.is_empty():
portrait = character.default_portrait
var info := {'character':character, 'portrait':portrait, 'same_scene':false}
if not portrait in character.portraits.keys():
print_debug('[Dialogic] Change to not-existing portrait will be ignored!')
return info
# Path to the scene to use.
var scene_path: String = character.portraits[portrait].get('scene', '')
var portrait_node: Node = null
var previous_portrait: Node = null
var portrait_count := character_node.get_child_count()
if portrait_count > 0:
previous_portrait = character_node.get_child(-1)
# Check if the scene is the same as the currently loaded scene.
if (not previous_portrait == null and
previous_portrait.get_meta('scene', '') == scene_path and
# Also check if the scene supports changing to the given portrait.
previous_portrait._should_do_portrait_update(character, portrait)):
portrait_node = previous_portrait
info['same_scene'] = true
else:
if ResourceLoader.exists(scene_path):
var packed_scene: PackedScene = load(scene_path)
if packed_scene:
portrait_node = packed_scene.instantiate()
else:
push_error('[Dialogic] Portrait node "' + str(scene_path) + '" for character [' + character.display_name + '] could not be loaded. Your portrait might not show up on the screen. Confirm the path is correct.')
if !portrait_node:
portrait_node = default_portrait_scene.instantiate()
portrait_node.set_meta('scene', scene_path)
if portrait_node:
portrait_node.set_meta('portrait', portrait)
character_node.set_meta('portrait', portrait)
DialogicUtil.apply_scene_export_overrides(portrait_node, character.portraits[portrait].get('export_overrides', {}))
if portrait_node.has_method('_update_portrait'):
portrait_node._update_portrait(character, portrait)
if not portrait_node.is_inside_tree():
character_node.add_child(portrait_node)
_update_portrait_transform(portrait_node)
## Handle Cross-Animating
if previous_portrait and previous_portrait != portrait_node:
if not fade_animation.is_empty() and fade_length > 0:
var fade_out := _animate_node(previous_portrait, fade_animation, fade_length, 1, true)
var _fade_in := _animate_node(portrait_node, fade_animation, fade_length, 1, false)
fade_out.finished.connect(previous_portrait.queue_free)
else:
previous_portrait.queue_free()
return info
## Changes the mirroring of the given portrait.
## Unless @force is false, this will take into consideration the character mirror,
## portrait mirror and portrait position mirror settings.
func _change_portrait_mirror(character_node: Node2D, mirrored := false, force := false) -> void:
var latest_portrait := character_node.get_child(-1)
if latest_portrait.has_method('_set_mirror'):
var character: DialogicCharacter = character_node.get_meta('character')
var current_portrait_info := character.get_portrait_info(character_node.get_meta('portrait'))
latest_portrait._set_mirror(force or (mirrored != character.mirror != character_node.get_parent().mirrored != current_portrait_info.get('mirror', false)))
func _change_portrait_extradata(character_node: Node2D, extra_data := "") -> void:
var latest_portrait := character_node.get_child(-1)
if latest_portrait.has_method('_set_extra_data'):
latest_portrait._set_extra_data(extra_data)
func _update_character_transform(character_node:Node, time := 0.0) -> void:
for child in character_node.get_children():
_update_portrait_transform(child, time)
func _update_portrait_transform(portrait_node: Node, time:float = 0.0) -> void:
var character_node: Node = portrait_node.get_parent()
var character: DialogicCharacter = character_node.get_meta('character')
var portrait_info: Dictionary = character.portraits.get(portrait_node.get_meta('portrait'), {})
# ignore the character scale on custom portraits that have 'ignore_char_scale' set to true
var apply_character_scale: bool = not portrait_info.get('ignore_char_scale', false)
var transform: Rect2 = character_node.get_parent().get_local_portrait_transform(
portrait_node._get_covered_rect(),
(character.scale * portrait_info.get('scale', 1))*int(apply_character_scale)+portrait_info.get('scale', 1)*int(!apply_character_scale))
var tween: Tween
if character_node.has_meta('move_tween'):
if character_node.get_meta('move_tween').is_running():
time = character_node.get_meta('move_time')-character_node.get_meta('move_tween').get_total_elapsed_time()
tween = character_node.get_meta('move_tween')
tween.stop()
if time == 0:
character_node.position = transform.position
portrait_node.position = character.offset + portrait_info.get('offset', Vector2())
portrait_node.scale = transform.size
else:
if not tween:
tween = character_node.create_tween().set_parallel().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
character_node.set_meta('move_tween', tween)
character_node.set_meta('move_time', time)
tween.tween_method(DialogicUtil.multitween.bind(character_node, "position", "base"), character_node.position, transform.position, time)
tween.tween_property(portrait_node, 'position',character.offset + portrait_info.get('offset', Vector2()), time)
tween.tween_property(portrait_node, 'scale', transform.size, time)
## Animates the node with the given animation.
## Is used both on the character node (most animations) and the portrait nodes (cross-fade animations)
func _animate_node(node: Node, animation_path: String, length: float, repeats := 1, is_reversed := false) -> DialogicAnimation:
if node.has_meta('animation_node') and is_instance_valid(node.get_meta('animation_node')):
node.get_meta('animation_node').queue_free()
var anim_script: Script = load(animation_path)
var anim_node := Node.new()
anim_node.set_script(anim_script)
anim_node = (anim_node as DialogicAnimation)
anim_node.node = node
anim_node.base_position = node.position
anim_node.base_scale = node.scale
anim_node.time = length
anim_node.repeats = repeats
anim_node.is_reversed = is_reversed
add_child(anim_node)
anim_node.animate()
node.set_meta("animation_path", animation_path)
node.set_meta("animation_length", length)
node.set_meta("animation_node", anim_node)
#if not is_silent:
#portrait_animating.emit(portrait_node.get_parent(), portrait_node, animation_path, length)
return anim_node
## Moves the given portrait to the given container.
func _move_character(character_node: Node2D, transform:="", time := 0.0, easing:= Tween.EASE_IN_OUT, trans:= Tween.TRANS_SINE) -> void:
var tween := character_node.create_tween().set_ease(easing).set_trans(trans).set_parallel()
if time == 0:
tween.kill()
tween = null
var container: DialogicNode_PortraitContainer = character_node.get_parent()
dialogic.PortraitContainers.move_container(container, transform, tween, time)
for portrait_node in character_node.get_children():
_update_portrait_transform(portrait_node, time)
## Changes the given portraits z_index.
func _change_portrait_z_index(character_node: Node, z_index:int, update_zindex:= true) -> void:
if update_zindex:
character_node.get_parent().set_meta('z_index', z_index)
var sorted_children := character_node.get_parent().get_parent().get_children()
sorted_children.sort_custom(z_sort_portrait_containers)
var idx := 0
for con in sorted_children:
con.get_parent().move_child(con, idx)
idx += 1
## Checks if [para, character] has joined the scene, if so, returns its
## active [DialogicPortrait] node.
##
## The difference between an active and inactive nodes is whether the node is
## the latest node. [br]
## If a portrait is fading/animating from portrait A and B, both will exist
## in the scene, but only the new portrait is active, even if it is not
## fully visible yet.
func get_character_portrait(character: DialogicCharacter) -> DialogicPortrait:
if is_character_joined(character):
var portrait_node: DialogicPortrait = dialogic.current_state_info['portraits'][character.resource_path].node.get_child(-1)
return portrait_node
return null
func z_sort_portrait_containers(con1: DialogicNode_PortraitContainer, con2: DialogicNode_PortraitContainer) -> bool:
if con1.get_meta('z_index', 0) < con2.get_meta('z_index', 0):
return true
return false
## Private method to remove a [param portrait_node].
func _remove_portrait(portrait_node: Node) -> void:
portrait_node.get_parent().remove_child(portrait_node)
portrait_node.queue_free()
## Gets the default animation length for joining characters
## If Auto-Skip is enabled, limits the time.
func _get_join_default_length() -> float:
var default_time: float = ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5)
if dialogic.Inputs.auto_skip.enabled:
default_time = min(default_time, dialogic.Inputs.auto_skip.time_per_event)
return default_time
## Gets the default animation length for leaving characters
## If Auto-Skip is enabled, limits the time.
func _get_leave_default_length() -> float:
var default_time: float = ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5)
if dialogic.Inputs.auto_skip.enabled:
default_time = min(default_time, dialogic.Inputs.auto_skip.time_per_event)
return default_time
## Checks multiple cases to return a valid portrait to use.
func get_valid_portrait(character:DialogicCharacter, portrait:String) -> String:
if character == null:
printerr('[Dialogic] Tried to use portrait "', portrait, '" on <null> character.')
dialogic.print_debug_moment()
return ""
if "{" in portrait and dialogic.has_subsystem("Expressions"):
var test: Variant = dialogic.Expressions.execute_string(portrait)
if test:
portrait = str(test)
if not portrait in character.portraits:
if not portrait.is_empty():
printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', DialogicResourceUtil.get_unique_identifier(character.resource_path), '". Using default portrait instead.')
dialogic.print_debug_moment()
portrait = character.default_portrait
if portrait.is_empty():
portrait = character.default_portrait
return portrait
#endregion
#region Character Methods
####################################################################################################
## The following methods are used to manage character portraits with the following rules:
## - a character can only be present once with these methods.
## Most of them will fail silently if the character isn't joined yet.
## Adds a character at a position and sets it's portrait.
## If the character is already joined it will only update, portrait, position, etc.
func join_character(character:DialogicCharacter, portrait:String, position_id:String, mirrored:= false, z_index:= 0, extra_data:= "", animation_name:= "", animation_length:= 0.0, animation_wait := false) -> Node:
if is_character_joined(character):
change_character_portrait(character, portrait)
if animation_name.is_empty():
animation_length = _get_join_default_length()
if animation_wait:
dialogic.current_state = DialogicGameHandler.States.ANIMATING
await get_tree().create_timer(animation_length).timeout
dialogic.current_state = DialogicGameHandler.States.IDLE
move_character(character, position_id, animation_length)
change_character_mirror(character, mirrored)
return
var container := dialogic.PortraitContainers.add_container(character.get_character_name())
var character_node := add_character(character, container, portrait, position_id)
if character_node == null:
return null
dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id, 'custom_mirror':mirrored}
_change_portrait_mirror(character_node, mirrored)
_change_portrait_extradata(character_node, extra_data)
_change_portrait_z_index(character_node, z_index)
var info := {'character':character}
info.merge(dialogic.current_state_info['portraits'][character.resource_path])
character_joined.emit(info)
if animation_name.is_empty():
animation_name = ProjectSettings.get_setting('dialogic/animations/join_default', "Fade In Up")
animation_length = _get_join_default_length()
animation_wait = ProjectSettings.get_setting('dialogic/animations/join_default_wait', true)
animation_name = DialogicPortraitAnimationUtil.guess_animation(animation_name, DialogicPortraitAnimationUtil.AnimationType.IN)
if animation_name and animation_length > 0:
var anim: DialogicAnimation = _animate_node(character_node, animation_name, animation_length)
if animation_wait:
dialogic.current_state = DialogicGameHandler.States.ANIMATING
await anim.finished
dialogic.current_state = DialogicGameHandler.States.IDLE
return character_node
func add_character(character:DialogicCharacter, container: DialogicNode_PortraitContainer, portrait:String, position_id:String) -> Node:
if is_character_joined(character):
printerr('[DialogicError] Cannot add a already joined character. If this is intended call _create_character_node manually.')
return null
portrait = get_valid_portrait(character, portrait)
if portrait.is_empty():
return null
if not character:
printerr('[DialogicError] Cannot call add_portrait() with null character.')
return null
var character_node := _create_character_node(character, container)
if character_node == null:
printerr('[Dialogic] Failed to join character to position ', position_id, ". Could not find position container.")
return null
dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id}
_move_character(character_node, position_id)
_change_portrait(character_node, portrait)
return character_node
## Changes the portrait of a character. Only works with joined characters.
func change_character_portrait(character: DialogicCharacter, portrait: String, fade_animation:="DEFAULT", fade_length := -1.0) -> void:
if not is_character_joined(character):
return
portrait = get_valid_portrait(character, portrait)
if dialogic.current_state_info.portraits[character.resource_path].portrait == portrait:
return
if fade_animation == "DEFAULT":
fade_animation = ProjectSettings.get_setting('dialogic/animations/cross_fade_default', "Fade Cross")
fade_length = ProjectSettings.get_setting('dialogic/animations/cross_fade_default_length', 0.5)
fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)
var info := _change_portrait(dialogic.current_state_info.portraits[character.resource_path].node, portrait, fade_animation, fade_length)
dialogic.current_state_info.portraits[character.resource_path].portrait = info.portrait
_change_portrait_mirror(
dialogic.current_state_info.portraits[character.resource_path].node,
dialogic.current_state_info.portraits[character.resource_path].get('custom_mirror', false)
)
character_portrait_changed.emit(info)
## Changes the mirror of the given character. Only works with joined characters
func change_character_mirror(character:DialogicCharacter, mirrored:= false, force:= false) -> void:
if !is_character_joined(character):
return
_change_portrait_mirror(dialogic.current_state_info.portraits[character.resource_path].node, mirrored, force)
dialogic.current_state_info.portraits[character.resource_path]['custom_mirror'] = mirrored
## Changes the z_index of a character. Only works with joined characters
func change_character_z_index(character:DialogicCharacter, z_index:int, update_zindex:= true) -> void:
if !is_character_joined(character):
return
_change_portrait_z_index(dialogic.current_state_info.portraits[character.resource_path].node, z_index, update_zindex)
if update_zindex:
dialogic.current_state_info.portraits[character.resource_path]['z_index'] = z_index
## Changes the extra data on the given character. Only works with joined characters
func change_character_extradata(character:DialogicCharacter, extra_data:="") -> void:
if !is_character_joined(character):
return
_change_portrait_extradata(dialogic.current_state_info.portraits[character.resource_path].node, extra_data)
dialogic.current_state_info.portraits[character.resource_path]['extra_data'] = extra_data
## Starts the given animation on the given character. Only works with joined characters
func animate_character(character: DialogicCharacter, animation_path: String, length: float, repeats := 1, is_reversed := false) -> DialogicAnimation:
if not is_character_joined(character):
return null
animation_path = DialogicPortraitAnimationUtil.guess_animation(animation_path)
var character_node: Node = dialogic.current_state_info.portraits[character.resource_path].node
return _animate_node(character_node, animation_path, length, repeats, is_reversed)
## Moves the given character to the given position. Only works with joined characters
func move_character(character:DialogicCharacter, position_id:String, time:= 0.0, easing:=Tween.EASE_IN_OUT, trans:=Tween.TRANS_SINE) -> void:
if !is_character_joined(character):
return
if dialogic.current_state_info.portraits[character.resource_path].position_id == position_id:
return
_move_character(dialogic.current_state_info.portraits[character.resource_path].node, position_id, time, easing, trans)
dialogic.current_state_info.portraits[character.resource_path].position_id = position_id
character_moved.emit({'character':character, 'position_id':position_id, 'time':time})
## Removes a character with a given animation or the default animation.
func leave_character(character: DialogicCharacter, animation_name:= "", animation_length:= 0.0, animation_wait := false) -> void:
if not is_character_joined(character):
return
if animation_name.is_empty():
animation_name = ProjectSettings.get_setting('dialogic/animations/leave_default', "Fade Out Down")
animation_length = _get_leave_default_length()
animation_wait = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true)
animation_name = DialogicPortraitAnimationUtil.guess_animation(animation_name, DialogicPortraitAnimationUtil.AnimationType.OUT)
if not animation_name.is_empty():
var character_node := get_character_node(character)
var animation := _animate_node(character_node, animation_name, animation_length, 1, true)
if animation_length > 0:
if animation_wait:
dialogic.current_state = DialogicGameHandler.States.ANIMATING
await animation.finished
dialogic.current_state = DialogicGameHandler.States.IDLE
remove_character(character)
else:
animation.finished.connect(func(): remove_character(character))
else:
remove_character(character)
## Removes all joined characters with a given animation or the default animation.
func leave_all_characters(animation_name:="", animation_length:=0.0, animation_wait := false) -> void:
for character in get_joined_characters():
await leave_character(character, animation_name, animation_length, animation_wait)
## Finds the character node for a [param character].
## Return `null` if the [param character] is not part of the scene.
func get_character_node(character: DialogicCharacter) -> Node:
if is_character_joined(character):
if is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].node):
return dialogic.current_state_info['portraits'][character.resource_path].node
return null
## Removes the given characters portrait.
## Only works with joined characters.
func remove_character(character: DialogicCharacter) -> void:
var character_node := get_character_node(character)
if is_instance_valid(character_node) and character_node is Node:
var container := character_node.get_parent()
container.get_parent().remove_child(container)
container.queue_free()
character_node.queue_free()
character_left.emit({'character': character})
dialogic.current_state_info['portraits'].erase(character.resource_path)
func get_current_character() -> DialogicCharacter:
if dialogic.current_state_info.get('speaker', null):
return load(dialogic.current_state_info.speaker)
return null
## Returns true if the given character is currently joined.
func is_character_joined(character: DialogicCharacter) -> bool:
if character == null or not character.resource_path in dialogic.current_state_info['portraits']:
return false
return true
## Returns a list of the joined charcters (as resources)
func get_joined_characters() -> Array[DialogicCharacter]:
var chars: Array[DialogicCharacter] = []
for char_path: String in dialogic.current_state_info.get('portraits', {}).keys():
chars.append(load(char_path))
return chars
## Returns a dictionary with info on a given character.
## Keys can be [joined, character, node (for the portrait node), position_id]
## Only joined is included (and false) for not joined characters
func get_character_info(character:DialogicCharacter) -> Dictionary:
if is_character_joined(character):
var info: Dictionary = dialogic.current_state_info['portraits'][character.resource_path]
info['joined'] = true
return info
else:
return {'joined':false}
#endregion
#region SPEAKER PORTRAIT CONTAINERS
####################################################################################################
## Updates all portrait containers set to SPEAKER.
func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void:
for container: Node in get_tree().get_nodes_in_group('dialogic_portrait_con_speaker'):
var just_joined := true
for character_node: Node in container.get_children():
if not character_node.get_meta('character') == speaker:
var leave_animation: String = ProjectSettings.get_setting('dialogic/animations/leave_default', "Fade Out")
leave_animation = DialogicPortraitAnimationUtil.guess_animation(leave_animation, DialogicPortraitAnimationUtil.AnimationType.OUT)
var leave_animation_length := _get_leave_default_length()
if leave_animation and leave_animation_length:
var animate_out := _animate_node(character_node, leave_animation, leave_animation_length, 1, true)
animate_out.finished.connect(character_node.queue_free)
else:
character_node.get_parent().remove_child(character_node)
character_node.queue_free()
else:
just_joined = false
if speaker == null or speaker.portraits.is_empty():
continue
if just_joined:
_create_character_node(speaker, container)
elif portrait.is_empty():
continue
if portrait.is_empty():
portrait = speaker.default_portrait
var character_node := container.get_child(-1)
var fade_animation: String = ProjectSettings.get_setting('dialogic/animations/cross_fade_default', "Fade Cross")
var fade_length: float = ProjectSettings.get_setting('dialogic/animations/cross_fade_default_length', 0.5)
fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)
if container.portrait_prefix+portrait in speaker.portraits:
portrait = container.portrait_prefix+portrait
_change_portrait(character_node, portrait, fade_animation, fade_length)
# if the character has no portraits _change_portrait won't actually add a child node
if character_node.get_child_count() == 0:
continue
if just_joined:
# Change speaker is called before the text is changed.
# In styles where the speaker is IN the textbox,
# this can mean the portrait container isn't sized correctly yet.
character_node.hide()
if not container.is_visible_in_tree():
await get_tree().process_frame
character_node.show()
var join_animation: String = ProjectSettings.get_setting('dialogic/animations/join_default', "Fade In Up")
join_animation = DialogicPortraitAnimationUtil.guess_animation(join_animation, DialogicPortraitAnimationUtil.AnimationType.IN)
var join_animation_length := _get_join_default_length()
if join_animation and join_animation_length:
_animate_node(character_node, join_animation, join_animation_length)
_change_portrait_mirror(character_node)
if speaker:
if speaker.resource_path != dialogic.current_state_info['speaker']:
if dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])):
dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight()
if speaker and is_character_joined(speaker):
dialogic.current_state_info['portraits'][speaker.resource_path].node.get_child(-1)._highlight()
elif dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])):
dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight()
#endregion
#region TEXT EFFECTS
####################################################################################################
## Called from the [portrait=something] text effect.
func text_effect_portrait(_text_node:Control, _skipped:bool, argument:String) -> void:
if argument:
if dialogic.current_state_info.get('speaker', null):
change_character_portrait(load(dialogic.current_state_info.speaker), argument)
change_speaker(load(dialogic.current_state_info.speaker), argument)
## Called from the [extra_data=something] text effect.
func text_effect_extradata(_text_node:Control, _skipped:bool, argument:String) -> void:
if argument:
if dialogic.current_state_info.get('speaker', null):
change_character_extradata(load(dialogic.current_state_info.speaker), argument)
#endregion

View File

@@ -0,0 +1 @@
uid://kgcapukun2he

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="31" height="22" viewBox="0 0 31 22" fill="none" version="1.1" id="svg4" sodipodi:docname="update_mirror.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs8" />
<sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="21.84375" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4">
<inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" />
</sodipodi:namedview>
<g inkscape:groupmode="layer" id="layer2" inkscape:label="Mirror" style="display:inline">
<path id="path8742" style="fill:#ffffff;fill-opacity:1;stroke-width:0.759831" d="m 10.967882,3.1351682 c 0.863708,-0.038595 2.312143,1.467492 2.312143,3.2198562 v 0 c 0,1.7523639 -1.491571,3.2198509 -2.312143,3.2198509" sodipodi:nodetypes="cssc" class="UnoptimicedTransforms" transform="matrix(1.1995187,0,0,1.1995187,-2.6822942,-1.4596729)" />
<path id="path16944" style="fill:#ffffff;fill-opacity:1;stroke-width:0.914608" d="m 7.5210109,10.015357 c -0.023367,7.6e-4 -0.046802,8.86e-4 -0.070302,0.0012 -0.9139878,0.009 -2.8630987,-1.7296781 -2.8630987,-3.8633837 0,-2.1337061 1.9491109,-3.8466572 2.8630988,-3.8634194 0.023497,-4.31e-4 0.046938,3.82e-4 0.070302,0.00113" sodipodi:nodetypes="csssc" />
<path id="path8744" style="display:inline;fill:#ffffff;fill-opacity:1;stroke-width:0.866873" d="m 10.468766,9.4004107 c 0.0017,-5.3e-6 0.0035,-8.8e-6 0.0051,-8.8e-6 1.704613,0 3.086415,2.9100071 3.086415,6.4996661 v 0 c 0,1.999827 -1.381802,1.999829 -3.086415,1.999829 h -0.0052" />
<path id="path16885" style="display:inline;fill:#ffffff;fill-opacity:1;stroke-width:0.874663" d="m 7.5210109,17.899897 c -1.7328976,-2e-6 -3.1367969,-0.002 -3.1367969,-1.999829 0,-3.586045 1.4039606,-6.4938047 3.1369101,-6.4996573" />
<path style="display:none;fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 4.3862586,18.660184 H 13.902208" id="path11459" />
<path style="display:none;fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 5.0553488,16.987459 -1.3381805,1.635554 1.3010089,1.970099" id="path11461" />
<path style="display:none;fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 13.567664,16.875944 0.854949,1.821412 -0.817777,1.9701" id="path11463" />
<path style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.37801;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0.981752" d="m 9.0131471,1.539982 -0.03659,16.903546" id="path16998" />
</g>
<path id="path26928" style="display:inline;fill:#ff4596;fill-opacity:1;stroke-width:0.613366" inkscape:label="path26928" d="m 17.19844,8.281361 c -0.272575,-8.2e-6 -0.500185,9.5e-6 -0.687018,0.012976 -0.194489,0.013249 -0.37819,0.041877 -0.556302,0.1156474 -0.4133,0.1711984 -0.741702,0.4995528 -0.912902,0.9128597 -0.08914,0.2151709 -0.113203,0.4419435 -0.122463,0.6884219 -0.0075,0.198126 -0.107439,0.363433 -0.25499,0.44863 -0.147562,0.08519 -0.340728,0.08912 -0.516018,-0.0037 -0.218061,-0.115251 -0.426533,-0.207727 -0.657444,-0.238134 -0.443521,-0.05839 -0.892077,0.06181 -1.246989,0.33413 -0.152944,0.117382 -0.269558,0.262171 -0.378298,0.423977 -0.104461,0.155439 -0.218268,0.352519 -0.354578,0.588618 l -0.01552,0.02687 c -0.136286,0.236087 -0.250093,0.433158 -0.332475,0.601337 -0.08575,0.17508 -0.152839,0.348492 -0.178013,0.53963 -0.05839,0.443529 0.06182,0.892086 0.334128,1.247025 0.141774,0.184685 0.326035,0.319018 0.534849,0.450213 0.167922,0.105499 0.261112,0.274802 0.2611,0.445256 -4e-6,0.170395 -0.09319,0.339684 -0.2611,0.445179 -0.208839,0.131218 -0.393112,0.265542 -0.534887,0.450286 -0.272334,0.354894 -0.39255,0.803431 -0.334165,1.246956 0.02519,0.191135 0.09222,0.364576 0.178014,0.53963 0.08239,0.168183 0.196157,0.365293 0.332477,0.601374 l 0.01552,0.02687 c 0.136299,0.236083 0.250113,0.433152 0.354577,0.588576 0.108764,0.161802 0.225385,0.306641 0.378336,0.423982 0.35491,0.272332 0.803465,0.392553 1.246989,0.334165 0.230896,-0.03043 0.439318,-0.122887 0.657371,-0.238137 0.175301,-0.09269 0.368508,-0.08873 0.516089,-0.0034 0.147577,0.08519 0.247617,0.250486 0.255026,0.448666 0.0092,0.246453 0.03333,0.473216 0.122463,0.688386 0.171199,0.413282 0.4996,0.741708 0.912902,0.912899 0.178115,0.0738 0.361807,0.102372 0.556303,0.115607 0.186836,0.01298 0.414443,0.01298 0.687018,0.01298 h 0.03098 c 0.272641,0 0.500197,0 0.687087,-0.01298 0.194489,-0.01325 0.378223,-0.04182 0.556344,-0.11562 0.413285,-0.171184 0.741671,-0.499617 0.912862,-0.912901 0.08914,-0.21517 0.113203,-0.441908 0.122409,-0.68842 0.0075,-0.198111 0.107425,-0.36345 0.254987,-0.448709 0.147577,-0.08522 0.340795,-0.08914 0.516092,0.0034 0.218051,0.115251 0.426474,0.207726 0.657404,0.2381 0.443522,0.05839 0.892099,-0.06174 1.246993,-0.334094 0.152961,-0.117441 0.269546,-0.262206 0.378297,-0.424009 0.104448,-0.155439 0.218252,-0.352497 0.354541,-0.588579 l 0.01569,-0.02687 c 0.136272,-0.236049 0.2501,-0.433192 0.332472,-0.601376 0.08573,-0.17506 0.152807,-0.348467 0.178015,-0.539596 0.05839,-0.443585 -0.06185,-0.89213 -0.334163,-1.247026 C 22.966705,15.585721 22.782388,15.451451 22.573601,15.320252 22.405679,15.214753 22.3125,15.045453 22.3125,14.875 c 0,-0.170395 0.09314,-0.339612 0.261028,-0.445112 0.208903,-0.131191 0.393243,-0.265477 0.534992,-0.450283 0.272333,-0.354903 0.392559,-0.803461 0.334166,-1.246992 -0.0252,-0.191132 -0.09228,-0.364516 -0.178014,-0.539595 -0.08236,-0.168164 -0.196157,-0.365264 -0.332511,-0.601337 l -0.01552,-0.02685 c -0.136362,-0.236031 -0.25012,-0.433148 -0.354576,-0.588582 -0.108763,-0.161803 -0.225359,-0.306611 -0.378332,-0.423976 -0.354895,-0.27233 -0.80344,-0.392521 -1.247026,-0.33413 -0.230874,0.03042 -0.439283,0.122886 -0.657331,0.238135 -0.17532,0.09266 -0.368517,0.08873 -0.516097,0.0034 -0.147577,-0.0852 -0.247635,-0.250542 -0.25506,-0.448696 C 19.499068,9.7645223 19.474933,9.5377607 19.385811,9.3226061 19.21463,8.9093853 18.886236,8.5810255 18.472951,8.4098311 18.294838,8.336061 18.111106,8.3074462 17.916607,8.2941837 17.729734,8.281208 17.502159,8.281208 17.229518,8.281208 Z m 0.01569,4.293575 c 1.270347,0 2.300127,1.029802 2.300127,2.300124 0,1.270344 -1.02978,2.300124 -2.300127,2.300124 -1.270314,0 -2.300123,-1.02978 -2.300123,-2.300124 0,-1.270322 1.029809,-2.300124 2.300123,-2.300124 z" />
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c735ss4h37y8i"
path="res://.godot/imported/update_mirror.svg-d6fa4832a7758cad02a7f32282433230.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/update_mirror.svg"
dest_files=["res://.godot/imported/update_mirror.svg-d6fa4832a7758cad02a7f32282433230.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="31" height="22" viewBox="0 0 31 22" fill="none" version="1.1" id="svg4" sodipodi:docname="update_portrait.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs8" />
<sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="23.75" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4">
<inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" />
</sodipodi:namedview>
<path fill-rule="evenodd" clip-rule="evenodd" d="m 9.9375,15.875 c 3.576743,0 6.476256,-2.960429 6.476256,-6.612317 0,-3.6518789 -2.899513,-6.612317 -6.476256,-6.612317 -3.5767353,0 -6.4762575,2.9604381 -6.4762575,6.612317 0,3.651888 2.8995222,6.612317 6.4762575,6.612317 z M 5.9096842,8.375405 c 0.332121,-0.339136 0.7825817,-0.5296473 1.2522769,-0.5296473 0.4696953,0 0.9201559,0.1905113 1.2522768,0.5296473 L 8.6852232,8.652084 9.339464,7.984097 9.0684787,7.7074187 C 8.5628126,7.1911667 7.8770324,6.9011417 7.1619611,6.9011417 c -0.7150713,0 -1.4008514,0.290025 -1.9064807,0.806277 L 4.9845045,7.984097 5.6386991,8.652084 Z M 12.713037,7.8457577 c -0.469713,0 -0.920183,0.1905113 -1.252322,0.5296473 L 11.189823,8.652084 10.535537,7.984097 10.806521,7.7074187 c 0.505703,-0.516252 1.191445,-0.806277 1.906516,-0.806277 0.715072,0 1.400815,0.290025 1.906519,0.806277 L 14.890541,7.984097 14.236254,8.652084 13.965361,8.375405 C 13.633222,8.036269 13.182751,7.8457577 12.713037,7.8457577 Z M 9.9375,13.985766 c -1.8503592,0 -3.2381288,-1.889233 -3.7007186,-2.833849 h 7.4014356 c -0.46259,0.944616 -1.850358,2.833849 -3.700717,2.833849 z" fill="#ffffff" id="path2" style="display:inline;stroke-width:0.934854" inkscape:label="Portrait" />
<path id="path26928" style="display:inline;fill:#ff4596;fill-opacity:1;stroke-width:0.613366" inkscape:label="path26928" d="m 19.13594,6.968861 c -0.272575,-8.2e-6 -0.500185,9.5e-6 -0.687018,0.012976 -0.194489,0.013249 -0.37819,0.041877 -0.556302,0.1156474 -0.4133,0.1711984 -0.741702,0.4995528 -0.912902,0.9128597 -0.08914,0.2151709 -0.113203,0.4419435 -0.122463,0.6884214 -0.0075,0.1981263 -0.107439,0.3634338 -0.25499,0.4486307 -0.147562,0.085189 -0.340728,0.089123 -0.516018,-0.00369 -0.218061,-0.1152514 -0.426533,-0.207727 -0.657444,-0.2381338 -0.443521,-0.058391 -0.892077,0.061805 -1.246989,0.3341293 -0.152944,0.1173821 -0.269558,0.2621716 -0.378298,0.4239769 -0.104461,0.1554392 -0.218268,0.3525194 -0.354578,0.5886184 l -0.01552,0.02687 c -0.136286,0.236087 -0.250093,0.433158 -0.332475,0.601337 -0.08575,0.17508 -0.152839,0.348492 -0.178013,0.53963 -0.05839,0.443529 0.06182,0.892086 0.334128,1.247025 0.141774,0.184685 0.326035,0.319018 0.534849,0.450213 0.167922,0.105499 0.261112,0.274802 0.2611,0.445256 -4e-6,0.170395 -0.09319,0.339684 -0.2611,0.445179 -0.208839,0.131218 -0.393112,0.265542 -0.534887,0.450286 -0.272334,0.354894 -0.39255,0.803431 -0.334165,1.246956 0.02519,0.191135 0.09222,0.364576 0.178014,0.53963 0.08239,0.168183 0.196157,0.365293 0.332477,0.601374 l 0.01552,0.02687 c 0.136299,0.236083 0.250113,0.433152 0.354577,0.588576 0.108764,0.161802 0.225385,0.306641 0.378336,0.423982 0.35491,0.272332 0.803465,0.392553 1.246989,0.334165 0.230896,-0.03043 0.439318,-0.122887 0.657371,-0.238137 0.175301,-0.09269 0.368508,-0.08873 0.516089,-0.0034 0.147577,0.08519 0.247617,0.250486 0.255026,0.448666 0.0092,0.246453 0.03333,0.473216 0.122463,0.688386 0.171199,0.413282 0.4996,0.741708 0.912902,0.912899 0.178115,0.0738 0.361807,0.102372 0.556303,0.115607 0.186836,0.01298 0.414443,0.01298 0.687018,0.01298 h 0.03098 c 0.272641,0 0.500197,0 0.687087,-0.01298 0.194489,-0.01325 0.378223,-0.04182 0.556344,-0.11562 0.413285,-0.171184 0.741671,-0.499617 0.912862,-0.912901 0.08914,-0.21517 0.113203,-0.441908 0.122409,-0.68842 0.0075,-0.198111 0.107425,-0.36345 0.254987,-0.448709 0.147577,-0.08522 0.340795,-0.08914 0.516092,0.0034 0.218051,0.115251 0.426474,0.207726 0.657404,0.2381 0.443522,0.05839 0.892099,-0.06174 1.246993,-0.334094 0.152961,-0.117441 0.269546,-0.262206 0.378297,-0.424009 0.104448,-0.155439 0.218252,-0.352497 0.354541,-0.588579 l 0.01569,-0.02687 c 0.136272,-0.236049 0.2501,-0.433192 0.332472,-0.601376 0.08573,-0.17506 0.152807,-0.348467 0.178015,-0.539596 0.05839,-0.443585 -0.06185,-0.89213 -0.334163,-1.247026 C 24.904205,14.273221 24.719888,14.138951 24.511101,14.007752 24.343179,13.902253 24.25,13.732953 24.25,13.5625 c 0,-0.170395 0.09314,-0.339612 0.261028,-0.445112 0.208903,-0.131191 0.393243,-0.265477 0.534992,-0.450283 0.272333,-0.354903 0.392559,-0.803461 0.334166,-1.246992 -0.0252,-0.191132 -0.09228,-0.364516 -0.178014,-0.539595 -0.08236,-0.168164 -0.196157,-0.365264 -0.332511,-0.601337 l -0.01552,-0.02685 C 24.717779,10.016297 24.604021,9.8191797 24.499565,9.6637461 24.390802,9.5019435 24.274206,9.3571349 24.121233,9.2397705 23.766338,8.9674396 23.317793,8.8472493 22.874207,8.9056399 22.643333,8.9360576 22.434924,9.0285264 22.216876,9.143775 22.041556,9.236435 21.848359,9.232501 21.700779,9.147185 21.553202,9.0619873 21.453144,8.8966429 21.445719,8.6984893 21.436568,8.4520223 21.412433,8.2252607 21.323311,8.0101061 21.15213,7.5968853 20.823736,7.2685255 20.410451,7.0973311 20.232338,7.023561 20.048606,6.9949462 19.854107,6.9816837 19.667234,6.968708 19.439659,6.968708 19.167018,6.968708 Z m 0.01569,4.293575 c 1.270347,0 2.300127,1.029802 2.300127,2.300124 0,1.270344 -1.02978,2.300124 -2.300127,2.300124 -1.270314,0 -2.300123,-1.02978 -2.300123,-2.300124 0,-1.270322 1.029809,-2.300124 2.300123,-2.300124 z" />
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://qx5bntelnslj"
path="res://.godot/imported/update_portrait.svg-b90fa6163d3720d34df8578ce2aa35e1.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/update_portrait.svg"
dest_files=["res://.godot/imported/update_portrait.svg-b90fa6163d3720d34df8578ce2aa35e1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="31" height="22" viewBox="0 0 31 22" fill="none" version="1.1" id="svg4" sodipodi:docname="update_position.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs8" />
<sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="23.84375" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4">
<inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" />
</sodipodi:namedview>
<path fill-rule="evenodd" clip-rule="evenodd" d="m 9.6329576,18.73815 c 4.1893454,0 7.5854694,-3.467473 7.5854694,-7.744834 0,-4.2773499 -3.396124,-7.7448337 -7.5854694,-7.7448337 -4.1893365,0 -7.5854711,3.4674838 -7.5854711,7.7448337 0,4.277361 3.3961346,7.744834 7.5854711,7.744834 z M 4.9152823,9.9540703 c 0.3890046,-0.397221 0.9166174,-0.6203616 1.4667592,-0.6203616 0.5501418,0 1.0777545,0.2231406 1.4667591,0.6203616 L 8.1661985,10.278137 8.9324937,9.4957421 8.6150957,9.1716755 C 8.0228222,8.5670037 7.2195858,8.2273042 6.3820415,8.2273042 c -0.8375444,0 -1.6407807,0.3396995 -2.233011,0.9443713 l -0.317387,0.3240666 0.766241,0.7823949 z M 12.883872,9.3337087 c -0.550163,0 -1.077787,0.2231406 -1.466813,0.6203616 L 11.099771,10.278137 10.333423,9.4957421 10.650819,9.1716755 c 0.592317,-0.6046718 1.395509,-0.9443713 2.233053,-0.9443713 0.837545,0 1.640738,0.3396995 2.233056,0.9443713 l 0.317397,0.3240666 -0.766349,0.7823949 -0.31729,-0.3240667 C 13.96166,9.5568493 13.434035,9.3337087 12.883872,9.3337087 Z M 9.6329576,16.52534 c -2.1672773,0 -3.7927356,-2.21281 -4.3345549,-3.319213 h 8.6691083 c -0.54182,1.106403 -2.167276,3.319213 -4.3345534,3.319213 z" fill="#ffffff" id="path2" style="display:none;stroke-width:1.09497" inkscape:label="Portrait" />
<path style="display:none;fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 17.531776,3.4508426 1.936447,3.0983145 3.063553,4.9016859 -5,8" id="path4576" sodipodi:nodetypes="cccc" />
<g inkscape:groupmode="layer" id="layer3" inkscape:label="Position" style="display:inline">
<path d="m 15.435727,5.7204944 c 0,2.118883 -1.717741,3.8365686 -3.836615,3.8365686 -2.1188833,0 -3.8365806,-1.7176856 -3.8365806,-3.8365686 0,-2.1188838 1.7176973,-3.8365775 3.8365806,-3.8365775 2.118874,0 3.836615,1.7176937 3.836615,3.8365775 z" fill="#ffffff" id="path19723" style="stroke-width:1.05506" />
<path d="m 16.202965,15.244259 c 0,1.985936 -2.061163,1.985936 -4.603853,1.985936 -2.542658,0 -4.6038923,0 -4.6038923,-1.985936 0,-3.564724 2.0612343,-6.454507 4.6038923,-6.454507 2.54269,0 4.603853,2.889783 4.603853,6.454507 z" fill="#ffffff" id="path19725" style="stroke-width:1.05506" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.744;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 7.2613947,9.27472 h -4" id="path22754" sodipodi:nodetypes="cc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 5.2613947,6.2747209 -2,2.9999991 2,3" id="path22756" sodipodi:nodetypes="ccc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.64208;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 15.767666,9.21222 h 3.487458" id="path25041" sodipodi:nodetypes="cc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.60066;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="M 17.511395,6.1617262 19.255124,9.21222 17.511395,12.262715" id="path25043" sodipodi:nodetypes="ccc" />
</g>
<path id="path26928" style="display:inline;fill:#ff4596;fill-opacity:1;stroke-width:0.613366" inkscape:label="path26928" d="m 20.13594,8.406361 c -0.272575,-8.2e-6 -0.500185,9.5e-6 -0.687018,0.012976 -0.194489,0.013249 -0.37819,0.041877 -0.556302,0.1156474 -0.4133,0.1711984 -0.741702,0.4995528 -0.912902,0.9128597 -0.08914,0.2151709 -0.113203,0.4419435 -0.122463,0.6884219 -0.0075,0.198126 -0.107439,0.363433 -0.25499,0.44863 -0.147562,0.08519 -0.340728,0.08912 -0.516018,-0.0037 -0.218061,-0.115251 -0.426533,-0.207727 -0.657444,-0.238134 -0.443521,-0.05839 -0.892077,0.06181 -1.246989,0.33413 -0.152944,0.117382 -0.269558,0.262171 -0.378298,0.423977 -0.104461,0.155439 -0.218268,0.352519 -0.354578,0.588618 l -0.01552,0.02687 c -0.136286,0.236087 -0.250093,0.433158 -0.332475,0.601337 -0.08575,0.17508 -0.152839,0.348492 -0.178013,0.53963 -0.05839,0.443529 0.06182,0.892086 0.334128,1.247025 0.141774,0.184685 0.326035,0.319018 0.534849,0.450213 0.167922,0.105499 0.261112,0.274802 0.2611,0.445256 -4e-6,0.170395 -0.09319,0.339684 -0.2611,0.445179 -0.208839,0.131218 -0.393112,0.265542 -0.534887,0.450286 -0.272334,0.354894 -0.39255,0.803431 -0.334165,1.246956 0.02519,0.191135 0.09222,0.364576 0.178014,0.53963 0.08239,0.168183 0.196157,0.365293 0.332477,0.601374 l 0.01552,0.02687 c 0.136299,0.236083 0.250113,0.433152 0.354577,0.588576 0.108764,0.161802 0.225385,0.306641 0.378336,0.423982 0.35491,0.272332 0.803465,0.392553 1.246989,0.334165 0.230896,-0.03043 0.439318,-0.122887 0.657371,-0.238137 0.175301,-0.09269 0.368508,-0.08873 0.516089,-0.0034 0.147577,0.08519 0.247617,0.250486 0.255026,0.448666 0.0092,0.246453 0.03333,0.473216 0.122463,0.688386 0.171199,0.413282 0.4996,0.741708 0.912902,0.912899 0.178115,0.0738 0.361807,0.102372 0.556303,0.115607 0.186836,0.01298 0.414443,0.01298 0.687018,0.01298 h 0.03098 c 0.272641,0 0.500197,0 0.687087,-0.01298 0.194489,-0.01325 0.378223,-0.04182 0.556344,-0.11562 0.413285,-0.171184 0.741671,-0.499617 0.912862,-0.912901 0.08914,-0.21517 0.113203,-0.441908 0.122409,-0.68842 0.0075,-0.198111 0.107425,-0.36345 0.254987,-0.448709 0.147577,-0.08522 0.340795,-0.08914 0.516092,0.0034 0.218051,0.115251 0.426474,0.207726 0.657404,0.2381 0.443522,0.05839 0.892099,-0.06174 1.246993,-0.334094 0.152961,-0.117441 0.269546,-0.262206 0.378297,-0.424009 0.104448,-0.155439 0.218252,-0.352497 0.354541,-0.588579 l 0.01569,-0.02687 c 0.136272,-0.236049 0.2501,-0.433192 0.332472,-0.601376 0.08573,-0.17506 0.152807,-0.348467 0.178015,-0.539596 0.05839,-0.443585 -0.06185,-0.89213 -0.334163,-1.247026 C 25.904205,15.710721 25.719888,15.576451 25.511101,15.445252 25.343179,15.339753 25.25,15.170453 25.25,15 c 0,-0.170395 0.09314,-0.339612 0.261028,-0.445112 0.208903,-0.131191 0.393243,-0.265477 0.534992,-0.450283 0.272333,-0.354903 0.392559,-0.803461 0.334166,-1.246992 -0.0252,-0.191132 -0.09228,-0.364516 -0.178014,-0.539595 -0.08236,-0.168164 -0.196157,-0.365264 -0.332511,-0.601337 l -0.01552,-0.02685 c -0.136362,-0.236031 -0.25012,-0.433148 -0.354576,-0.588582 -0.108763,-0.161803 -0.225359,-0.306611 -0.378332,-0.423976 -0.354895,-0.27233 -0.80344,-0.392521 -1.247026,-0.33413 -0.230874,0.03042 -0.439283,0.122886 -0.657331,0.238135 -0.17532,0.09266 -0.368517,0.08873 -0.516097,0.0034 -0.147577,-0.0852 -0.247635,-0.250542 -0.25506,-0.448696 C 22.436568,9.8895223 22.412433,9.6627607 22.323311,9.4476061 22.15213,9.0343853 21.823736,8.7060255 21.410451,8.5348311 21.232338,8.461061 21.048606,8.4324462 20.854107,8.4191837 20.667234,8.406208 20.439659,8.406208 20.167018,8.406208 Z m 0.01569,4.293575 c 1.270347,0 2.300127,1.029802 2.300127,2.300124 0,1.270344 -1.02978,2.300124 -2.300127,2.300124 -1.270314,0 -2.300123,-1.02978 -2.300123,-2.300124 0,-1.270322 1.029809,-2.300124 2.300123,-2.300124 z" />
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="31" height="22" viewBox="0 0 31 22" fill="none" version="1.1" id="svg4" sodipodi:docname="update_position.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs8" />
<sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="23.84375" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4">
<inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" />
</sodipodi:namedview>
<path fill-rule="evenodd" clip-rule="evenodd" d="m 9.6329576,18.73815 c 4.1893454,0 7.5854694,-3.467473 7.5854694,-7.744834 0,-4.2773499 -3.396124,-7.7448337 -7.5854694,-7.7448337 -4.1893365,0 -7.5854711,3.4674838 -7.5854711,7.7448337 0,4.277361 3.3961346,7.744834 7.5854711,7.744834 z M 4.9152823,9.9540703 c 0.3890046,-0.397221 0.9166174,-0.6203616 1.4667592,-0.6203616 0.5501418,0 1.0777545,0.2231406 1.4667591,0.6203616 L 8.1661985,10.278137 8.9324937,9.4957421 8.6150957,9.1716755 C 8.0228222,8.5670037 7.2195858,8.2273042 6.3820415,8.2273042 c -0.8375444,0 -1.6407807,0.3396995 -2.233011,0.9443713 l -0.317387,0.3240666 0.766241,0.7823949 z M 12.883872,9.3337087 c -0.550163,0 -1.077787,0.2231406 -1.466813,0.6203616 L 11.099771,10.278137 10.333423,9.4957421 10.650819,9.1716755 c 0.592317,-0.6046718 1.395509,-0.9443713 2.233053,-0.9443713 0.837545,0 1.640738,0.3396995 2.233056,0.9443713 l 0.317397,0.3240666 -0.766349,0.7823949 -0.31729,-0.3240667 C 13.96166,9.5568493 13.434035,9.3337087 12.883872,9.3337087 Z M 9.6329576,16.52534 c -2.1672773,0 -3.7927356,-2.21281 -4.3345549,-3.319213 h 8.6691083 c -0.54182,1.106403 -2.167276,3.319213 -4.3345534,3.319213 z" fill="#ffffff" id="path2" style="display:none;stroke-width:1.09497" inkscape:label="Portrait" />
<path style="display:none;fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 17.531776,3.4508426 1.936447,3.0983145 3.063553,4.9016859 -5,8" id="path4576" sodipodi:nodetypes="cccc" />
<g inkscape:groupmode="layer" id="layer3" inkscape:label="Position" style="display:inline">
<path d="m 15.435727,5.7204944 c 0,2.118883 -1.717741,3.8365686 -3.836615,3.8365686 -2.1188833,0 -3.8365806,-1.7176856 -3.8365806,-3.8365686 0,-2.1188838 1.7176973,-3.8365775 3.8365806,-3.8365775 2.118874,0 3.836615,1.7176937 3.836615,3.8365775 z" fill="#ffffff" id="path19723" style="stroke-width:1.05506" />
<path d="m 16.202965,15.244259 c 0,1.985936 -2.061163,1.985936 -4.603853,1.985936 -2.542658,0 -4.6038923,0 -4.6038923,-1.985936 0,-3.564724 2.0612343,-6.454507 4.6038923,-6.454507 2.54269,0 4.603853,2.889783 4.603853,6.454507 z" fill="#ffffff" id="path19725" style="stroke-width:1.05506" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.744;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 7.2613947,9.27472 h -4" id="path22754" sodipodi:nodetypes="cc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 5.2613947,6.2747209 -2,2.9999991 2,3" id="path22756" sodipodi:nodetypes="ccc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.64208;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="m 15.767666,9.21222 h 3.487458" id="path25041" sodipodi:nodetypes="cc" />
<path style="fill:none;stroke:#ffffff;stroke-width:1.60066;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.236785;stroke-opacity:1" d="M 17.511395,6.1617262 19.255124,9.21222 17.511395,12.262715" id="path25043" sodipodi:nodetypes="ccc" />
</g>
<path id="path26928" style="display:inline;fill:#ff4596;fill-opacity:1;stroke-width:0.613366" inkscape:label="path26928" d="m 20.13594,8.406361 c -0.272575,-8.2e-6 -0.500185,9.5e-6 -0.687018,0.012976 -0.194489,0.013249 -0.37819,0.041877 -0.556302,0.1156474 -0.4133,0.1711984 -0.741702,0.4995528 -0.912902,0.9128597 -0.08914,0.2151709 -0.113203,0.4419435 -0.122463,0.6884219 -0.0075,0.198126 -0.107439,0.363433 -0.25499,0.44863 -0.147562,0.08519 -0.340728,0.08912 -0.516018,-0.0037 -0.218061,-0.115251 -0.426533,-0.207727 -0.657444,-0.238134 -0.443521,-0.05839 -0.892077,0.06181 -1.246989,0.33413 -0.152944,0.117382 -0.269558,0.262171 -0.378298,0.423977 -0.104461,0.155439 -0.218268,0.352519 -0.354578,0.588618 l -0.01552,0.02687 c -0.136286,0.236087 -0.250093,0.433158 -0.332475,0.601337 -0.08575,0.17508 -0.152839,0.348492 -0.178013,0.53963 -0.05839,0.443529 0.06182,0.892086 0.334128,1.247025 0.141774,0.184685 0.326035,0.319018 0.534849,0.450213 0.167922,0.105499 0.261112,0.274802 0.2611,0.445256 -4e-6,0.170395 -0.09319,0.339684 -0.2611,0.445179 -0.208839,0.131218 -0.393112,0.265542 -0.534887,0.450286 -0.272334,0.354894 -0.39255,0.803431 -0.334165,1.246956 0.02519,0.191135 0.09222,0.364576 0.178014,0.53963 0.08239,0.168183 0.196157,0.365293 0.332477,0.601374 l 0.01552,0.02687 c 0.136299,0.236083 0.250113,0.433152 0.354577,0.588576 0.108764,0.161802 0.225385,0.306641 0.378336,0.423982 0.35491,0.272332 0.803465,0.392553 1.246989,0.334165 0.230896,-0.03043 0.439318,-0.122887 0.657371,-0.238137 0.175301,-0.09269 0.368508,-0.08873 0.516089,-0.0034 0.147577,0.08519 0.247617,0.250486 0.255026,0.448666 0.0092,0.246453 0.03333,0.473216 0.122463,0.688386 0.171199,0.413282 0.4996,0.741708 0.912902,0.912899 0.178115,0.0738 0.361807,0.102372 0.556303,0.115607 0.186836,0.01298 0.414443,0.01298 0.687018,0.01298 h 0.03098 c 0.272641,0 0.500197,0 0.687087,-0.01298 0.194489,-0.01325 0.378223,-0.04182 0.556344,-0.11562 0.413285,-0.171184 0.741671,-0.499617 0.912862,-0.912901 0.08914,-0.21517 0.113203,-0.441908 0.122409,-0.68842 0.0075,-0.198111 0.107425,-0.36345 0.254987,-0.448709 0.147577,-0.08522 0.340795,-0.08914 0.516092,0.0034 0.218051,0.115251 0.426474,0.207726 0.657404,0.2381 0.443522,0.05839 0.892099,-0.06174 1.246993,-0.334094 0.152961,-0.117441 0.269546,-0.262206 0.378297,-0.424009 0.104448,-0.155439 0.218252,-0.352497 0.354541,-0.588579 l 0.01569,-0.02687 c 0.136272,-0.236049 0.2501,-0.433192 0.332472,-0.601376 0.08573,-0.17506 0.152807,-0.348467 0.178015,-0.539596 0.05839,-0.443585 -0.06185,-0.89213 -0.334163,-1.247026 C 25.904205,15.710721 25.719888,15.576451 25.511101,15.445252 25.343179,15.339753 25.25,15.170453 25.25,15 c 0,-0.170395 0.09314,-0.339612 0.261028,-0.445112 0.208903,-0.131191 0.393243,-0.265477 0.534992,-0.450283 0.272333,-0.354903 0.392559,-0.803461 0.334166,-1.246992 -0.0252,-0.191132 -0.09228,-0.364516 -0.178014,-0.539595 -0.08236,-0.168164 -0.196157,-0.365264 -0.332511,-0.601337 l -0.01552,-0.02685 c -0.136362,-0.236031 -0.25012,-0.433148 -0.354576,-0.588582 -0.108763,-0.161803 -0.225359,-0.306611 -0.378332,-0.423976 -0.354895,-0.27233 -0.80344,-0.392521 -1.247026,-0.33413 -0.230874,0.03042 -0.439283,0.122886 -0.657331,0.238135 -0.17532,0.09266 -0.368517,0.08873 -0.516097,0.0034 -0.147577,-0.0852 -0.247635,-0.250542 -0.25506,-0.448696 C 22.436568,9.8895223 22.412433,9.6627607 22.323311,9.4476061 22.15213,9.0343853 21.823736,8.7060255 21.410451,8.5348311 21.232338,8.461061 21.048606,8.4324462 20.854107,8.4191837 20.667234,8.406208 20.439659,8.406208 20.167018,8.406208 Z m 0.01569,4.293575 c 1.270347,0 2.300127,1.029802 2.300127,2.300124 0,1.270344 -1.02978,2.300124 -2.300127,2.300124 -1.270314,0 -2.300123,-1.02978 -2.300123,-2.300124 0,-1.270322 1.029809,-2.300124 2.300123,-2.300124 z" />
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://deu1h3rsp3p8y"
path="res://.godot/imported/update_position.svg.2023_09_23_08_37_47.0.svg-d66e70a05e9de92a595b70fa88fca0d4.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/update_position.svg.2023_09_23_08_37_47.0.svg"
dest_files=["res://.godot/imported/update_position.svg.2023_09_23_08_37_47.0.svg-d66e70a05e9de92a595b70fa88fca0d4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://rslog4jar4gu"
path="res://.godot/imported/update_position.svg-4be50608dc0768e715ff659ca62cf187.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/update_position.svg"
dest_files=["res://.godot/imported/update_position.svg-4be50608dc0768e715ff659ca62cf187.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="31" height="22" viewBox="0 0 31 22" fill="none" version="1.1" id="svg4" sodipodi:docname="update_z_index.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs8" />
<sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="23.84375" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4">
<inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" />
</sodipodi:namedview>
<g inkscape:groupmode="layer" id="layer1" inkscape:label="Z_Index" style="display:inline" transform="matrix(0.90945359,0,0,0.90945359,0.03431233,1.6441321)">
<path style="fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 4.0841125,6.8565774 V 16.856578 H 12.084113 V 6.8565774 Z" id="path6916" sodipodi:nodetypes="ccccc" />
<path style="fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 12.084113,13.856578 h 3 V 3.8565774 H 7.0841125 v 3" id="path6920" sodipodi:nodetypes="ccccc" />
<path style="fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 15.084113,10.856578 h 3 V 0.8565774 h -8.000001 v 3" id="path6922" sodipodi:nodetypes="ccccc" />
<path style="fill:none;fill-opacity:0.980952;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="m 6.5841125,9.8565775 h 3 l -3,4.0000005 h 3" id="path6924" sodipodi:nodetypes="cccc" />
</g>
<path id="path26928" style="display:inline;fill:#ff4596;fill-opacity:1;stroke-width:0.613366" inkscape:label="path26928" d="m 19.07344,7.281361 c -0.272575,-8.2e-6 -0.500185,9.5e-6 -0.687018,0.012976 -0.194489,0.013249 -0.37819,0.041877 -0.556302,0.1156474 -0.4133,0.1711984 -0.741702,0.4995528 -0.912902,0.9128597 -0.08914,0.2151709 -0.113203,0.4419435 -0.122463,0.6884214 -0.0075,0.1981263 -0.107439,0.3634338 -0.25499,0.4486307 -0.147562,0.085189 -0.340728,0.089123 -0.516018,-0.00369 -0.218061,-0.1152514 -0.426533,-0.207727 -0.657444,-0.2381338 -0.443521,-0.058391 -0.892077,0.061805 -1.246989,0.3341293 -0.152944,0.1173821 -0.269558,0.2621716 -0.378298,0.4239769 -0.104461,0.1554394 -0.218268,0.3525194 -0.354578,0.5886184 l -0.01552,0.02687 c -0.136286,0.236087 -0.250093,0.433158 -0.332475,0.601337 -0.08575,0.17508 -0.152839,0.348492 -0.178013,0.53963 -0.05839,0.443529 0.06182,0.892086 0.334128,1.247025 0.141774,0.184685 0.326035,0.319018 0.534849,0.450213 0.167922,0.105499 0.261112,0.274802 0.2611,0.445256 -4e-6,0.170395 -0.09319,0.339684 -0.2611,0.445179 -0.208839,0.131218 -0.393112,0.265542 -0.534887,0.450286 -0.272334,0.354894 -0.39255,0.803431 -0.334165,1.246956 0.02519,0.191135 0.09222,0.364576 0.178014,0.53963 0.08239,0.168183 0.196157,0.365293 0.332477,0.601374 l 0.01552,0.02687 c 0.136299,0.236083 0.250113,0.433152 0.354577,0.588576 0.108764,0.161802 0.225385,0.306641 0.378336,0.423982 0.35491,0.272332 0.803465,0.392553 1.246989,0.334165 0.230896,-0.03043 0.439318,-0.122887 0.657371,-0.238137 0.175301,-0.09269 0.368508,-0.08873 0.516089,-0.0034 0.147577,0.08519 0.247617,0.250486 0.255026,0.448666 0.0092,0.246453 0.03333,0.473216 0.122463,0.688386 0.171199,0.413282 0.4996,0.741708 0.912902,0.912899 0.178115,0.0738 0.361807,0.102372 0.556303,0.115607 0.186836,0.01298 0.414443,0.01298 0.687018,0.01298 h 0.03098 c 0.272641,0 0.500197,0 0.687087,-0.01298 0.194489,-0.01325 0.378223,-0.04182 0.556344,-0.11562 0.413285,-0.171184 0.741671,-0.499617 0.912862,-0.912901 0.08914,-0.21517 0.113203,-0.441908 0.122409,-0.68842 0.0075,-0.198111 0.107425,-0.36345 0.254987,-0.448709 0.147577,-0.08522 0.340795,-0.08914 0.516092,0.0034 0.218051,0.115251 0.426474,0.207726 0.657404,0.2381 0.443522,0.05839 0.892099,-0.06174 1.246993,-0.334094 0.152961,-0.117441 0.269546,-0.262206 0.378297,-0.424009 0.104448,-0.155439 0.218252,-0.352497 0.354541,-0.588579 l 0.01569,-0.02687 c 0.136272,-0.236049 0.2501,-0.433192 0.332472,-0.601376 0.08573,-0.17506 0.152807,-0.348467 0.178015,-0.539596 0.05839,-0.443585 -0.06185,-0.89213 -0.334163,-1.247026 C 24.841705,14.585721 24.657388,14.451451 24.448601,14.320252 24.280679,14.214753 24.1875,14.045453 24.1875,13.875 c 0,-0.170395 0.09314,-0.339612 0.261028,-0.445112 0.208903,-0.131191 0.393243,-0.265477 0.534992,-0.450283 0.272333,-0.354903 0.392559,-0.803461 0.334166,-1.246992 -0.0252,-0.191132 -0.09228,-0.364516 -0.178014,-0.539595 -0.08236,-0.168164 -0.196157,-0.365264 -0.332511,-0.601337 l -0.01552,-0.02685 C 24.655279,10.328797 24.541521,10.13168 24.437065,9.9762461 24.328302,9.8144435 24.211706,9.6696349 24.058733,9.5522705 23.703838,9.2799396 23.255293,9.1597493 22.811707,9.2181399 22.580833,9.2485576 22.372424,9.3410264 22.154376,9.456275 21.979056,9.548935 21.785859,9.545001 21.638279,9.459685 21.490702,9.3744873 21.390644,9.2091429 21.383219,9.0109893 21.374068,8.7645223 21.349933,8.5377607 21.260811,8.3226061 21.08963,7.9093853 20.761236,7.5810255 20.347951,7.4098311 20.169838,7.336061 19.986106,7.3074462 19.791607,7.2941837 19.604734,7.281208 19.377159,7.281208 19.104518,7.281208 Z m 0.01569,4.293575 c 1.270347,0 2.300127,1.029802 2.300127,2.300124 0,1.270344 -1.02978,2.300124 -2.300127,2.300124 -1.270314,0 -2.300123,-1.02978 -2.300123,-2.300124 0,-1.270322 1.029809,-2.300124 2.300123,-2.300124 z" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5xa3i541igyk"
path="res://.godot/imported/update_z_index.svg-204064db9241de79bf8dfd23af57c6c6.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Character/update_z_index.svg"
dest_files=["res://.godot/imported/update_z_index.svg-204064db9241de79bf8dfd23af57c6c6.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true