]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Resources/timeline.gd
Squashed commit of the following:
[wolf-seeking-sheep.git] / addons / dialogic / Resources / timeline.gd
1 @tool
2 extends Resource
3 class_name DialogicTimeline
4
5 ## Resource that defines a list of events.
6 ## It can store them as text and load them from text too.
7
8 var events: Array = []
9 var events_processed := false
10
11
12 ## Method used for printing timeline resources identifiably
13 func _to_string() -> String:
14         return "[DialogicTimeline:{file}]".format({"file":resource_path})
15
16
17 ## Helper method
18 func get_event(index:int) -> Variant:
19         if index >= len(events):
20                 return null
21         return events[index]
22
23
24 ## Parses the lines as seperate events and insert them in an array,
25 ## so they can be converted to DialogicEvent's when processed later
26 func from_text(text:String) -> void:
27         events = text.split('\n', true)
28         events_processed = false
29
30
31 ## Stores all events in their text format and returns them as a string
32 func as_text() -> String:
33         var result := ""
34
35         if events_processed:
36                 var indent := 0
37                 for idx in range(0, len(events)):
38                         var event: DialogicEvent = events[idx]
39
40                         if event.event_name == 'End Branch':
41                                 indent -= 1
42                                 continue
43
44                         if event != null:
45                                 for i in event.empty_lines_above:
46                                         result += "\t".repeat(indent)+"\n"
47                                 result += "\t".repeat(indent)+event.event_node_as_text.replace('\n', "\n"+"\t".repeat(indent)) + "\n"
48                         if event.can_contain_events:
49                                 indent += 1
50                         if indent < 0:
51                                 indent = 0
52         else:
53                 for event in events:
54                         result += str(event)+"\n"
55
56                 result.trim_suffix('\n')
57
58         return result.strip_edges()
59
60
61 ## Method that loads all the event resources from the strings, if it wasn't done before
62 func process() -> void:
63         if typeof(events[0]) == TYPE_STRING:
64                 events_processed = false
65
66         # if the timeline is already processed
67         if events_processed:
68                 for event in events:
69                         event.event_node_ready = true
70                 return
71
72         var event_cache := DialogicResourceUtil.get_event_cache()
73         var end_event := DialogicEndBranchEvent.new()
74
75         var prev_indent := ""
76         var processed_events := []
77
78         # this is needed to add an end branch event even to empty conditions/choices
79         var prev_was_opener := false
80
81         var lines := events
82         var idx := -1
83         var empty_lines := 0
84         while idx < len(lines)-1:
85                 idx += 1
86
87                 # make sure we are using the string version, in case this was already converted
88                 var line := ""
89                 if typeof(lines[idx]) == TYPE_STRING:
90                         line = lines[idx]
91                 else:
92                         line = lines[idx].event_node_as_text
93
94                 ## Ignore empty lines, but record them in @empty_lines
95                 var line_stripped: String = line.strip_edges(true, false)
96                 if line_stripped.is_empty():
97                         empty_lines += 1
98                         continue
99
100                 ## Add an end event if the indent is smaller then previously
101                 var indent: String = line.substr(0,len(line)-len(line_stripped))
102                 if len(indent) < len(prev_indent):
103                         for i in range(len(prev_indent)-len(indent)):
104                                 processed_events.append(end_event.duplicate())
105                 ## Add an end event if the indent is the same but the previous was an opener
106                 ## (so for example choice that is empty)
107                 if prev_was_opener and len(indent) <= len(prev_indent):
108                         processed_events.append(end_event.duplicate())
109
110                 prev_indent = indent
111
112                 ## Now we process the event into a resource
113                 ## by checking on each event if it recognizes this string
114                 var event_content: String = line_stripped
115                 var event: DialogicEvent
116                 for i in event_cache:
117                         if i._test_event_string(event_content):
118                                 event = i.duplicate()
119                                 break
120
121                 event.empty_lines_above = empty_lines
122                 # add the following lines until the event says it's full or there is an empty line
123                 while !event.is_string_full_event(event_content):
124                         idx += 1
125                         if idx == len(lines):
126                                 break
127
128                         var following_line_stripped: String = lines[idx].strip_edges(true, false)
129
130                         if following_line_stripped.is_empty():
131                                 break
132
133                         event_content += "\n"+following_line_stripped
134
135                 event._load_from_string(event_content)
136                 event.event_node_as_text = event_content
137
138                 processed_events.append(event)
139                 prev_was_opener = event.can_contain_events
140                 empty_lines = 0
141
142         if !prev_indent.is_empty():
143                 for i in range(len(prev_indent)):
144                         processed_events.append(end_event.duplicate())
145
146         events = processed_events
147         events_processed = true
148
149
150 ## This method makes sure that all events in a timeline are correctly reset
151 func clean() -> void:
152         if not events_processed:
153                 return
154         reference()
155         # This is necessary because otherwise INTERNAL GODOT ONESHOT CONNECTIONS
156         # are disconnected before they can disconnect themselves.
157         await Engine.get_main_loop().process_frame
158
159         for event:DialogicEvent in events:
160                 for con_in in event.get_incoming_connections():
161                         con_in.signal.disconnect(con_in.callable)
162
163                 for sig in event.get_signal_list():
164                         for con_out in event.get_signal_connection_list(sig.name):
165                                 con_out.signal.disconnect(con_out.callable)
166         unreference()