]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Modules/Character/subsystem_containers.gd
Initial Godot project with Dialogic 2.0-Alpha-17
[wolf-seeking-sheep.git] / addons / dialogic / Modules / Character / subsystem_containers.gd
1 extends DialogicSubsystem
2
3 ## Subsystem that manages portrait positions.
4
5 signal position_changed(info: Dictionary)
6
7
8 var transform_regex := r"(?<part>position|pos|size|siz|rotation|rot)\W*=(?<value>((?!(pos|siz|rot)).)*)"
9
10 #region STATE
11 ####################################################################################################
12
13
14 #endregion
15
16
17 #region MAIN METHODS
18 ####################################################################################################
19
20 func get_container(position_id: String) -> DialogicNode_PortraitContainer:
21         for portrait_position:DialogicNode_PortraitContainer in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'):
22                 if portrait_position.is_visible_in_tree() and portrait_position.is_container(position_id):
23                         return portrait_position
24         return null
25
26
27 func get_containers(position_id: String) -> Array[DialogicNode_PortraitContainer]:
28         return get_tree().get_nodes_in_group(&'dialogic_portrait_con_position').filter(
29                 func(node:DialogicNode_PortraitContainer):
30                         return node.is_visible_in_tree() and node.is_container(position_id))
31
32
33 func get_container_container() -> CanvasItem:
34         var any_portrait_container := get_tree().get_first_node_in_group(&'dialogic_portrait_con_position')
35         if any_portrait_container:
36                 return any_portrait_container.get_parent()
37         return null
38
39
40 ## Creates a new portrait container node.
41 ## It will copy it's size and most settings from the first p_container in the tree.
42 ## It will be added as a sibling of the first p_container in the tree.
43 func add_container(position_id: String) -> DialogicNode_PortraitContainer:
44         var example_position := get_tree().get_first_node_in_group(&'dialogic_portrait_con_position')
45         if example_position:
46                 var new_position := DialogicNode_PortraitContainer.new()
47                 example_position.get_parent().add_child(new_position)
48                 new_position.name = "Portrait_"+position_id.validate_node_name()
49                 copy_container_setup(example_position, new_position)
50                 new_position.container_ids = [position_id]
51                 position_changed.emit({&'change':'added', &'container_node':new_position, &'position_id':position_id})
52                 return new_position
53         return null
54
55
56 ## Moves the [container] to the [destionation] (using [tween] and [time]).
57 ## The destination can be a position_id (e.g. "center") or translation, roataion and scale.
58 ## When moving to a preset container, then some more will be "copied" (e.g. anchors, etc.)
59 func move_container(container:DialogicNode_PortraitContainer, destination:String, tween:Tween = null, time:float=1.0) -> void:
60         var target_position: Vector2 = container.position + container._get_origin_position()
61         var target_rotation: float = container.rotation
62         var target_size: Vector2 = container.size
63
64         var destination_container := get_container(destination)
65         if destination_container:
66                 container.set_meta("target_container", destination_container)
67                 target_position = destination_container.position + destination_container._get_origin_position()
68                 target_rotation = destination_container.rotation_degrees
69                 target_size = destination_container.size
70         else:
71                 var regex := RegEx.create_from_string(transform_regex)
72                 for found in regex.search_all(destination):
73                         match found.get_string('part'):
74                                 'pos', 'position':
75                                         target_position = str_to_vector(found.get_string("value"), target_position)
76                                 'rot', 'rotation':
77                                         target_rotation = float(found.get_string("value"))
78                                 'siz', 'size':
79                                         target_size = str_to_vector(found.get_string("value"), target_size)
80
81         translate_container(container, target_position, false, tween, time)
82         rotate_container(container, target_rotation, false, tween, time)
83         resize_container(container, target_size, false, tween, time)
84
85         if destination_container:
86                 if time:
87                         tween.finished.connect(func():
88                                 if container.has_meta("target_container"):
89                                         if container.get_meta("target_container") == destination_container:
90                                                 copy_container_setup(destination_container, container)
91                                 )
92                 else:
93                         copy_container_setup(destination_container, container)
94
95
96 func copy_container_setup(from:DialogicNode_PortraitContainer, to:DialogicNode_PortraitContainer) -> void:
97         to.ignore_resize = true
98         to.layout_mode = from.layout_mode
99         to.anchors_preset = from.anchors_preset
100         to.anchor_bottom = from.anchor_bottom
101         to.anchor_left = from.anchor_left
102         to.anchor_right = from.anchor_right
103         to.anchor_top = from.anchor_top
104         to.offset_bottom = from.offset_bottom
105         to.offset_top = from.offset_top
106         to.offset_right = from.offset_right
107         to.offset_left = from.offset_left
108         to.size_mode = from.size_mode
109         to.origin_anchor = from.origin_anchor
110         to.ignore_resize = false
111         to.update_portrait_transforms()
112
113
114 ## Translates the given container.
115 ## The given translation should be the target position of the ORIGIN point, not the container!
116 func translate_container(container:DialogicNode_PortraitContainer, translation:Variant, relative := false, tween:Tween=null, time:float=1.0) -> void:
117         if !container.has_meta(&'default_translation'):
118                 container.set_meta(&'default_translation', container.position + container._get_origin_position())
119
120         var final_translation: Vector2
121         if typeof(translation) == TYPE_STRING:
122                 final_translation = str_to_vector(translation, container.position+container._get_origin_position())
123         elif typeof(translation) == TYPE_VECTOR2:
124                 final_translation = translation
125
126         if relative:
127                 final_translation += container.position
128         else:
129                 final_translation -= container._get_origin_position()
130
131         if tween:
132                 tween.tween_method(DialogicUtil.multitween.bind(container, "position", "base"), container.position, final_translation, time)
133                 if not tween.finished.is_connected(save_position_container):
134                         tween.finished.connect(save_position_container.bind(container))
135         else:
136                 container.position = final_translation
137                 save_position_container(container)
138         position_changed.emit({&'change':'moved', &'container_node':container})
139
140
141 func rotate_container(container:DialogicNode_PortraitContainer, rotation:float, relative := false, tween:Tween=null, time:float=1.0) -> void:
142         if !container.has_meta(&'default_rotation'):
143                 container.set_meta(&'default_rotation', container.rotation_degrees)
144
145         var final_rotation := rotation
146
147         if relative:
148                 final_rotation += container.rotation_degrees
149
150         container.pivot_offset = container._get_origin_position()
151
152         if tween:
153                 tween.tween_property(container, 'rotation_degrees', final_rotation, time)
154                 if not tween.finished.is_connected(save_position_container):
155                         tween.finished.connect(save_position_container.bind(container))
156         else:
157                 container.rotation_degrees = final_rotation
158                 save_position_container(container)
159
160         position_changed.emit({&'change':'rotated', &'container_node':container})
161
162
163 func resize_container(container: DialogicNode_PortraitContainer, rect_size: Variant, relative := false, tween:Tween=null, time:float=1.0) -> void:
164         if !container.has_meta(&'default_size'):
165                 container.set_meta(&'default_size', container.size)
166
167         var final_rect_resize: Vector2
168         if typeof(rect_size) == TYPE_STRING:
169                 final_rect_resize = str_to_vector(rect_size, container.size)
170         elif typeof(rect_size) == TYPE_VECTOR2:
171                 final_rect_resize = rect_size
172
173         if relative:
174                 final_rect_resize += container.rect_size
175
176         var relative_position_change := container._get_origin_position()-container._get_origin_position(final_rect_resize)
177
178         if tween:
179                 tween.tween_method(DialogicUtil.multitween.bind(container, "position", "resize_move"), Vector2(), relative_position_change, time)
180                 tween.tween_property(container, 'size', final_rect_resize, time)
181                 if not tween.finished.is_connected(save_position_container):
182                         tween.finished.connect(save_position_container.bind(container))
183         else:
184                 container.position = container.position + relative_position_change
185                 container.size = final_rect_resize
186                 save_position_container(container)
187
188         position_changed.emit({&'change':'resized', &'container_node':container})
189
190
191 func save_position_container(container: DialogicNode_PortraitContainer) -> void:
192         if not dialogic.current_state_info.has('portrait_containers'):
193                 dialogic.current_state_info['portrait_containers'] = {}
194
195         var info := {
196                 "container_ids" : container.container_ids,
197                 "position" : container.position,
198                 "rotation" : container.rotation_degrees,
199                 "size" : container.size,
200                 "pivot_mode" : container.pivot_mode,
201                 "pivot_value" : container.pivot_value,
202                 "origin_anchor" : container.origin_anchor,
203                 "anchor_left" : container.anchor_left,
204                 "anchor_right" : container.anchor_right,
205                 "anchor_top" : container.anchor_top,
206                 "anchor_bottom" : container.anchor_bottom,
207                 "offset_left" : container.offset_left,
208                 "offset_right" : container.offset_right,
209                 "offset_top" : container.offset_top,
210                 "offset_bottom" : container.offset_bottom,
211         }
212
213         dialogic.current_state_info.portrait_containers[container.container_ids[0]] = info
214
215
216 func load_position_container(position_id: String) -> DialogicNode_PortraitContainer:
217         # First check whether the container already exists:
218         var container := get_container(position_id)
219         if container:
220                 return container
221
222         if not dialogic.current_state_info.has('portrait_containers') or not dialogic.current_state_info.portrait_containers.has(position_id):
223                 return null
224
225         var info: Dictionary = dialogic.current_state_info.portrait_containers[position_id]
226         container = add_container(position_id)
227
228         if not container:
229                 return null
230
231         container.container_ids = info.container_ids
232         container.position = info.position
233         container.rotation = info.rotation
234         container.size = info.size
235         container.pivot_mode = info.pivot_mode
236         container.pivot_value = info.pivot_value
237         container.origin_anchor = info.origin_anchor
238         container.anchor_left = info.anchor_left
239         container.anchor_right = info.anchor_right
240         container.anchor_top = info.anchor_top
241         container.anchor_bottom = info.anchor_bottom
242         container.offset_left = info.offset_left
243         container.offset_right = info.offset_right
244         container.offset_top = info.offset_top
245         container.offset_bottom = info.offset_bottom
246
247         return container
248
249
250 func str_to_vector(input: String, base_vector:=Vector2()) -> Vector2:
251         var vector_regex := RegEx.create_from_string(r"(?<part>x|y)\s*(?<number>(-|\+)?(\d|\.|)*)(\s*(?<type>%|px))?")
252         var vec := base_vector
253         for i in vector_regex.search_all(input):
254                 var value := float(i.get_string(&'number'))
255                 match i.get_string(&'type'):
256                         'px':
257                                 pass # Keep values as they are
258                         '%', _:
259                                 match i.get_string(&'part'):
260                                         'x': value *= get_viewport().get_visible_rect().size.x
261                                         'y': value *= get_viewport().get_visible_rect().size.y
262
263                 match i.get_string(&'part'):
264                         'x': vec.x = value
265                         'y': vec.y = value
266         return vec
267
268
269 func vector_to_str(vec:Vector2) -> String:
270         return "x" + str(vec.x) + "px y" + str(vec.y) + "px"
271
272
273 func reset_all_containers(time:= 0.0, tween:Tween = null) -> void:
274         for container in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'):
275                 reset_container(container, time, tween)
276
277
278 func reset_container(container:DialogicNode_PortraitContainer, time := 0.0, tween: Tween = null ) -> void:
279         if container.has_meta(&'default_translation'):
280                 translate_container(container, vector_to_str(container.get_meta(&'default_translation')), false, tween, time)
281         if container.has_meta(&'default_rotation'):
282                 rotate_container(container, container.get_meta(&'default_rotation'), false, tween, time)
283         if container.has_meta(&'default_size'):
284                 resize_container(container, vector_to_str(container.get_meta(&'default_size')), false, tween, time)