]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Modules/History/subsystem_history.gd
Squashed commit of the following:
[wolf-seeking-sheep.git] / addons / dialogic / Modules / History / subsystem_history.gd
1 extends DialogicSubsystem
2
3 ## Subsystem that manages history storing.
4
5 signal open_requested
6 signal close_requested
7
8
9 ## Simple history that stores limited information
10 ## Used for the history display
11 var simple_history_enabled := false
12 var simple_history_save := false
13 var simple_history_content : Array[Dictionary] = []
14 signal simple_history_changed
15
16 ## Whether to keep a history of every Dialogic event encountered.
17 var full_event_history_enabled := false
18 var full_event_history_save := false
19
20 ## The full history of all Dialogic events encountered.
21 ## Requires [member full_event_history_enabled] to be true.
22 var full_event_history_content: Array[DialogicEvent] = []
23
24 ## Emitted if a new event has been inserted into the full event history.
25 signal full_event_history_changed
26
27 ## Read text history
28 ## Stores which text events and choices have already been visited
29 var visited_event_history_enabled := false
30
31 ## A history of visited Dialogic events.
32 var visited_event_history_content := {}
33
34 ## Whether the last event has been encountered for the first time.
35 var _visited_last_event := false
36
37 ## Emitted if an encountered timeline event has been inserted into the visited
38 ## event history.
39 ##
40 ## This will trigger only once per unique event instance.
41 signal visited_event
42
43 ## Emitted if an encountered timeline event has not been visited before.
44 signal unvisited_event
45
46 ## Used to store [member visited_event_history_content] in the global info file.
47 ## You can change this to a custom name if you want to use a different key
48 ## in the global save info file.
49 var visited_event_save_key := "visited_event_history_content"
50
51 ## Whether to automatically save the already-visited history on auto-save.
52 var save_visited_history_on_autosave := false:
53         set(value):
54                 save_visited_history_on_autosave = value
55                 _update_saved_connection(value)
56
57
58 ## Whether to automatically save the already-visited history on manual save.
59 var save_visited_history_on_save := false:
60         set(value):
61                 save_visited_history_on_save = value
62                 _update_saved_connection(value)
63
64
65 ## Starts and stops the connection to the [subsystem Save] subsystem's [signal saved] signal.
66 func _update_saved_connection(to_connect: bool) -> void:
67         if to_connect:
68                 if not DialogicUtil.autoload().Save.saved.is_connected(_on_save):
69                         DialogicUtil.autoload().Save.saved.connect(_on_save)
70
71         else:
72                 if DialogicUtil.autoload().Save.saved.is_connected(_on_save):
73                         DialogicUtil.autoload().Save.saved.disconnect(_on_save)
74
75
76 #region INITIALIZE
77 ####################################################################################################
78
79 func _ready() -> void:
80         dialogic.event_handled.connect(store_full_event)
81         dialogic.event_handled.connect(_check_seen)
82
83         simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', simple_history_enabled)
84         simple_history_save = ProjectSettings.get_setting('dialogic/history/simple_history_save', simple_history_save)
85         full_event_history_enabled = ProjectSettings.get_setting('dialogic/history/full_history_enabled', full_event_history_enabled)
86         full_event_history_save = ProjectSettings.get_setting('dialogic/history/full_history_save', full_event_history_save)
87         visited_event_history_enabled = ProjectSettings.get_setting('dialogic/history/visited_event_history_enabled', visited_event_history_enabled)
88
89
90
91 func _on_save(info: Dictionary) -> void:
92         var is_autosave: bool = info["is_autosave"]
93
94         var save_on_autosave := save_visited_history_on_autosave and is_autosave
95         var save_on_save := save_visited_history_on_save and not is_autosave
96
97         if save_on_save or save_on_autosave:
98                 save_visited_history()
99
100
101 func post_install() -> void:
102         save_visited_history_on_autosave = ProjectSettings.get_setting('dialogic/history/save_on_autosave', save_visited_history_on_autosave)
103         save_visited_history_on_save = ProjectSettings.get_setting('dialogic/history/save_on_save', save_visited_history_on_save)
104
105
106 func clear_game_state(clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
107         if clear_flag == DialogicGameHandler.ClearFlags.FULL_CLEAR:
108                 if simple_history_save:
109                         simple_history_content = []
110                         dialogic.current_state_info.erase("history_simple")
111                 if full_event_history_save:
112                         full_event_history_content = []
113                         dialogic.current_state_info.erase("history_full")
114
115
116 func load_game_state(load_flag := LoadFlags.FULL_LOAD) -> void:
117         if load_flag == LoadFlags.FULL_LOAD:
118                 if simple_history_save and dialogic.current_state_info.has("history_simple"):
119                         simple_history_content.assign(dialogic.current_state_info["history_simple"])
120
121                 if full_event_history_save and dialogic.current_state_info.has("history_full"):
122                         full_event_history_content = []
123
124                         for event_text in dialogic.current_state_info["history_full"]:
125                                 var event: DialogicEvent
126                                 for i in DialogicResourceUtil.get_event_cache():
127                                         if i.is_valid_event(event_text):
128                                                 event = i.duplicate()
129                                                 break
130                                 event.from_text(event_text)
131                                 full_event_history_content.append(event)
132
133
134 func save_game_state() -> void:
135         if simple_history_save:
136                 dialogic.current_state_info["history_simple"] = Array(simple_history_content)
137         else:
138                 dialogic.current_state_info.erase("history_simple")
139         if full_event_history_save:
140                 dialogic.current_state_info["history_full"] = []
141                 for event in full_event_history_content:
142                         dialogic.current_state_info["history_full"].append(event.to_text())
143         else:
144                 dialogic.current_state_info.erase("history_full")
145
146
147 func open_history() -> void:
148         open_requested.emit()
149
150
151 func close_history() -> void:
152         close_requested.emit()
153
154 #endregion
155
156
157 #region SIMPLE HISTORY
158 ####################################################################################################
159
160 func store_simple_history_entry(text:String, event_type:String, extra_info := {}) -> void:
161         if !simple_history_enabled: return
162         extra_info['text'] = text
163         extra_info['event_type'] = event_type
164         simple_history_content.append(extra_info)
165         simple_history_changed.emit()
166
167
168 func get_simple_history() -> Array:
169         return simple_history_content
170
171 #endregion
172
173
174 #region FULL EVENT HISTORY
175 ####################################################################################################
176
177 ## Called on each event.
178 func store_full_event(event: DialogicEvent) -> void:
179         if !full_event_history_enabled: return
180         full_event_history_content.append(event)
181         full_event_history_changed.emit()
182
183
184 #region ALREADY READ HISTORY
185 ####################################################################################################
186
187 ## Takes the current timeline event and creates a unique key for it.
188 ## Uses the timeline resource path as well.
189 func _current_event_key() -> String:
190         var resource_path := dialogic.current_timeline.resource_path
191         var event_index := dialogic.current_event_idx
192         var event_key := _get_event_key(event_index, resource_path)
193
194         return event_key
195
196 ## Composes an event key from the event index and the timeline path.
197 ## If either of these variables are in an invalid state, the resulting
198 ## key may be wrong.
199 ## There are no safety checks in place.
200 func _get_event_key(event_index: int, timeline_path: String) -> String:
201         var event_idx := str(event_index)
202         var event_key := timeline_path + event_idx
203
204         return event_key
205
206
207 ## Called if an event is marked as visited.
208 func mark_event_as_visited(event_index := dialogic.current_event_idx, timeline := dialogic.current_timeline) -> void:
209         if !visited_event_history_enabled:
210                 return
211
212         var event_key := _get_event_key(event_index, timeline.resource_path)
213
214         visited_event_history_content[event_key] = event_index
215
216
217 ## Called on each event, but we filter for Text events.
218 func _check_seen(event: DialogicEvent) -> void:
219         if !visited_event_history_enabled:
220                 return
221
222         # At this point, we only care about Text events.
223         # There may be a more elegant way of filtering events.
224         # Especially since custom events require this event name.
225         if event.event_name != "Text":
226                 return
227
228         var event_key := _current_event_key()
229
230         if event_key in visited_event_history_content:
231                 visited_event.emit()
232                 _visited_last_event = true
233
234         else:
235                 unvisited_event.emit()
236                 _visited_last_event = false
237
238
239 ## Whether the last event has been visited for the first time or not.
240 ## This will return `true` exactly once for each unique timeline event instance.
241 func has_last_event_been_visited() -> bool:
242         return _visited_last_event
243
244
245 ## If called with with no arguments, the method will return whether
246 ## the last encountered event was visited before.
247 ##
248 ## Otherwise, if [param event_index] and [param timeline] are passed,
249 ## the method will check if the event from that given timeline has been
250 ## visited yet.
251 ##
252 ## If no [param timeline] is passed, the current timeline will be used.
253 ## If there is no current timeline, `false` will be returned.
254 ##
255 ## If no [param event_index] is passed, the current event index will be used.
256 func has_event_been_visited(event_index := dialogic.current_event_idx, timeline := dialogic.current_timeline) -> bool:
257         if timeline == null:
258                 return false
259
260         var event_key := _get_event_key(event_index, timeline.resource_path)
261         var visited := event_key in visited_event_history_content
262
263         return visited
264
265
266 ## Saves all seen events to the global info file.
267 ## This can be useful when the player saves the game.
268 ## In visual novels, callings this at the end of a route can be useful, as the
269 ## player may not save the game.
270 ##
271 ## Be aware, this won't add any events but completely overwrite the already saved ones.
272 ##
273 ## Relies on the [subsystem Save] subsystem.
274 func save_visited_history() -> void:
275         DialogicUtil.autoload().Save.set_global_info(visited_event_save_key, visited_event_history_content)
276
277
278 ## Loads the seen events from the global info save file.
279 ## Calling this when a game gets loaded may be useful.
280 ##
281 ## Relies on the [subsystem Save] subsystem.
282 func load_visited_history() -> void:
283         visited_event_history_content = get_saved_visited_history()
284
285
286 ## Returns the saved already-visited history from the global info save file.
287 ## If none exist in the global info file, returns an empty dictionary.
288 ##
289 ## Relies on the [subsystem Save] subsystem.
290 func get_saved_visited_history() -> Dictionary:
291         return DialogicUtil.autoload().Save.get_global_info(visited_event_save_key, {})
292
293
294 ## Resets the already-visited history in the global info save file.
295 ## If [param reset_property] is true, it will also reset the already-visited
296 ## history in the Dialogic Autoload.
297 ##
298 ## Relies on the [subsystem Save] subsystem.
299 func reset_visited_history(reset_property := true) -> void:
300         DialogicUtil.autoload().Save.set_global_info(visited_event_save_key, {})
301
302         if reset_property:
303                 visited_event_history_content = {}
304
305 #endregion