Add addons
This commit is contained in:
parent
2c862b2235
commit
3367bad24f
527 changed files with 36197 additions and 0 deletions
556
Sprout/addons/dialogic/Other/DialogicGameHandler.gd
Normal file
556
Sprout/addons/dialogic/Other/DialogicGameHandler.gd
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
extends Node
|
||||
|
||||
enum states {IDLE, SHOWING_TEXT, ANIMATING, AWAITING_CHOICE, WAITING}
|
||||
|
||||
var current_timeline: Variant = null
|
||||
var current_timeline_events: Array = []
|
||||
var timeline_jump_stack: Array = []
|
||||
var character_directory: Dictionary = {}
|
||||
var timeline_directory: Dictionary = {}
|
||||
var _event_script_cache: Array[DialogicEvent] = []
|
||||
|
||||
var current_state: Variant = null:
|
||||
get:
|
||||
return current_state
|
||||
set(new_state):
|
||||
current_state = new_state
|
||||
emit_signal('state_changed', new_state)
|
||||
|
||||
var paused := false:
|
||||
set(value):
|
||||
paused = value
|
||||
if paused:
|
||||
for subsystem in get_children():
|
||||
if subsystem.has_method('pause'):
|
||||
subsystem.pause()
|
||||
dialogic_paused.emit()
|
||||
else:
|
||||
for subsystem in get_children():
|
||||
if subsystem.has_method('resume'):
|
||||
subsystem.resume()
|
||||
dialogic_resumed.emit()
|
||||
|
||||
signal dialogic_paused
|
||||
signal dialogic_resumed
|
||||
|
||||
var current_event_idx: int = 0
|
||||
var current_state_info: Dictionary = {}
|
||||
|
||||
signal state_changed(new_state)
|
||||
signal timeline_ended()
|
||||
signal timeline_started()
|
||||
signal event_handled(resource)
|
||||
|
||||
signal signal_event(argument)
|
||||
signal text_signal(argument)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
rebuild_character_directory()
|
||||
rebuild_timeline_directory()
|
||||
|
||||
collect_subsystems()
|
||||
|
||||
clear()
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
## TIMELINE+EVENT HANDLING
|
||||
################################################################################
|
||||
func start_timeline(timeline_resource:Variant, label_or_idx:Variant = "") -> void:
|
||||
# load the resource if only the path is given
|
||||
if typeof(timeline_resource) == TYPE_STRING:
|
||||
#check the lookup table if it's not a full file name
|
||||
if timeline_resource.contains("res://"):
|
||||
timeline_resource = load(timeline_resource)
|
||||
else:
|
||||
timeline_resource = load(find_timeline(timeline_resource))
|
||||
if timeline_resource == null:
|
||||
assert(false, "There was an error loading this timeline. Check the filename, and the timeline for errors")
|
||||
|
||||
timeline_resource = process_timeline(timeline_resource)
|
||||
|
||||
current_timeline = timeline_resource
|
||||
current_timeline_events = current_timeline.get_events()
|
||||
current_event_idx = -1
|
||||
|
||||
if typeof(label_or_idx) == TYPE_STRING:
|
||||
if label_or_idx:
|
||||
jump_to_label(label_or_idx)
|
||||
elif typeof(label_or_idx) == TYPE_INT:
|
||||
if label_or_idx >-1:
|
||||
current_event_idx = label_or_idx -1
|
||||
|
||||
|
||||
|
||||
emit_signal('timeline_started')
|
||||
handle_next_event()
|
||||
|
||||
|
||||
# Preloader function, prepares a timeline and returns an object to hold for later
|
||||
## TODO: Question: why is this taking a variant and then only allowing a string?
|
||||
func preload_timeline(timeline_resource:Variant) -> Variant:
|
||||
# I think ideally this should be on a new thread, will test
|
||||
if typeof(timeline_resource) == TYPE_STRING:
|
||||
timeline_resource = load(timeline_resource)
|
||||
if timeline_resource == null:
|
||||
assert(false, "There was an error loading this timeline. Check the filename, and the timeline for errors")
|
||||
else:
|
||||
timeline_resource = process_timeline(timeline_resource)
|
||||
return timeline_resource
|
||||
return false
|
||||
|
||||
|
||||
func end_timeline() -> void:
|
||||
current_timeline = null
|
||||
current_timeline_events = []
|
||||
clear()
|
||||
emit_signal("timeline_ended")
|
||||
|
||||
|
||||
func handle_next_event(ignore_argument:Variant = "") -> void:
|
||||
handle_event(current_event_idx+1)
|
||||
|
||||
|
||||
func handle_event(event_index:int) -> void:
|
||||
if not current_timeline:
|
||||
return
|
||||
|
||||
if paused:
|
||||
await dialogic_resumed
|
||||
|
||||
if event_index >= len(current_timeline_events):
|
||||
if timeline_jump_stack.size() > 0:
|
||||
pop_from_jump_stack()
|
||||
event_index = current_event_idx+1
|
||||
else:
|
||||
end_timeline()
|
||||
return
|
||||
|
||||
#actually process the event now, since we didnt earlier at runtime
|
||||
#this needs to happen before we create the copy DialogicEvent variable, so it doesn't throw an error if not ready
|
||||
if current_timeline_events[event_index]['event_node_ready'] == false:
|
||||
current_timeline_events[event_index]._load_from_string(current_timeline_events[event_index]['event_node_as_text'])
|
||||
|
||||
current_event_idx = event_index
|
||||
|
||||
#print("\n[D] Handle Event ", event_index, ": ", event)
|
||||
if current_timeline_events[event_index].continue_at_end:
|
||||
#print(" -> WILL AUTO CONTINUE!")
|
||||
if not current_timeline_events[event_index].event_finished.is_connected(handle_next_event):
|
||||
current_timeline_events[event_index].event_finished.connect(handle_next_event, CONNECT_ONE_SHOT)
|
||||
|
||||
if has_subsystem('History'):
|
||||
self.History.add_event_to_history(current_timeline.resource_path, event_index, current_timeline_events[event_index])
|
||||
|
||||
current_timeline_events[event_index].execute(self)
|
||||
emit_signal('event_handled', current_timeline_events[event_index])
|
||||
|
||||
|
||||
func jump_to_label(label:String) -> void:
|
||||
if label.is_empty():
|
||||
current_event_idx = 0
|
||||
return
|
||||
var idx: int = -1
|
||||
while true:
|
||||
idx += 1
|
||||
var event: Variant = current_timeline.get_event(idx)
|
||||
if not event:
|
||||
idx = current_event_idx
|
||||
break
|
||||
if event is DialogicLabelEvent and event.name == label:
|
||||
break
|
||||
current_event_idx = idx
|
||||
|
||||
func push_to_jump_stack() -> void:
|
||||
var current_point:Dictionary = {}
|
||||
current_point['timeline'] = current_timeline
|
||||
current_point['index'] = current_event_idx
|
||||
timeline_jump_stack.push_front(current_point)
|
||||
|
||||
func pop_from_jump_stack() -> void:
|
||||
var stack_point:Dictionary = timeline_jump_stack.pop_front()
|
||||
current_timeline = stack_point['timeline']
|
||||
current_timeline_events = current_timeline.get_events()
|
||||
current_event_idx = stack_point['index']
|
||||
|
||||
|
||||
func clear() -> bool:
|
||||
for subsystem in get_children():
|
||||
subsystem.clear_game_state()
|
||||
|
||||
# Clearing existing Dialogic main nodes
|
||||
for i in get_tree().get_nodes_in_group('dialogic_main_node'):
|
||||
i.queue_free()
|
||||
|
||||
# Resetting variables
|
||||
current_timeline = null
|
||||
current_event_idx = -1
|
||||
current_timeline_events = []
|
||||
current_state = states.IDLE
|
||||
return true
|
||||
|
||||
|
||||
################################################################################
|
||||
## STATE
|
||||
################################################################################
|
||||
|
||||
func execute_condition(condition:String) -> bool:
|
||||
var regex: RegEx = RegEx.new()
|
||||
regex.compile('{(\\w.*)}')
|
||||
var result := regex.search_all(condition)
|
||||
if result:
|
||||
for res in result:
|
||||
var r_string: String = res.get_string()
|
||||
var value: String = self.VAR.get_variable(r_string)
|
||||
if !value.is_valid_float():
|
||||
value = '"'+value+'"'
|
||||
condition = condition.replace(r_string, value)
|
||||
var expr: Expression = Expression.new()
|
||||
# this doesn't work currently, not sure why. However you can still use autoloads in conditions
|
||||
# you have to put them in {} brackets tho. E.g. `if {MyAutoload.my_var} > 20:`
|
||||
# var autoload_names: Array = []
|
||||
# var autoloads: Array = []
|
||||
# for c in get_tree().root.get_children():
|
||||
# autoloads.append(c)
|
||||
# autoload_names.append(c.name)
|
||||
# expr.parse(condition, autoload_names)
|
||||
# if expr.execute(autoloads, self):
|
||||
expr.parse(condition)
|
||||
if expr.execute([], self):
|
||||
return true
|
||||
if expr.has_execute_failed():
|
||||
printerr('Dialogic: Condition failed to execute: ', expr.get_error_text())
|
||||
return false
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING & LOADING
|
||||
################################################################################
|
||||
func get_full_state() -> Dictionary:
|
||||
if current_timeline:
|
||||
current_state_info['current_event_idx'] = current_event_idx
|
||||
current_state_info['current_timeline'] = current_timeline.resource_path
|
||||
else:
|
||||
current_state_info['current_event_idx'] = -1
|
||||
current_state_info['current_timeline'] = null
|
||||
if has_subsystem('Portraits'):
|
||||
current_state_info['current_portrait_positions'] = self.Portraits.current_positions
|
||||
current_state_info['default_portrait_positions'] = self.Portraits._default_positions
|
||||
if has_subsystem('History'):
|
||||
if self.History.full_history_enabled:
|
||||
self.History.strip_events_from_full_history()
|
||||
current_state_info['full_history'] = self.History.full_history
|
||||
if self.History.text_read_history_enabled:
|
||||
current_state_info['text_read_history'] = self.History.text_read_history
|
||||
|
||||
return current_state_info
|
||||
|
||||
|
||||
func load_full_state(state_info:Dictionary) -> void:
|
||||
for subsystem in get_children():
|
||||
subsystem.clear_game_state()
|
||||
current_state_info = state_info
|
||||
if current_state_info.get('current_timeline', null):
|
||||
start_timeline(current_state_info.current_timeline, current_state_info.get('current_event_idx', 0))
|
||||
if has_subsystem('Portraits'):
|
||||
if current_state_info.get('current_portrait_positions', null):
|
||||
self.Portraits.current_positions = current_state_info['current_portrait_positions']
|
||||
self.Portraits._default_positions = current_state_info['default_portrait_positions']
|
||||
if has_subsystem('History'):
|
||||
if self.History.full_history_enabled:
|
||||
self.History.full_history = current_state_info['full_history']
|
||||
if self.History.text_read_history_enabled:
|
||||
self.History.text_read_history = current_state_info['text_read_history']
|
||||
for subsystem in get_children():
|
||||
subsystem.load_game_state()
|
||||
|
||||
################################################################################
|
||||
## SUB-SYTSEMS
|
||||
################################################################################
|
||||
func collect_subsystems() -> void:
|
||||
# This also builds the event script cache as well
|
||||
_event_script_cache = []
|
||||
|
||||
for indexer in DialogicUtil.get_indexers():
|
||||
|
||||
# build event cache
|
||||
for event in indexer._get_events():
|
||||
if not 'event_end_branch.gd' in event and not 'event_text.gd' in event:
|
||||
_event_script_cache.append(load(event).new())
|
||||
|
||||
# build the subsystems (only at runtime)
|
||||
if !Engine.is_editor_hint():
|
||||
for subsystem in indexer._get_subsystems():
|
||||
add_subsytsem(subsystem.name, subsystem.script)
|
||||
|
||||
# Events are checked in order while testing them. EndBranch needs to be first, Text needs to be last
|
||||
_event_script_cache.push_front(DialogicEndBranchEvent.new())
|
||||
_event_script_cache.push_back(DialogicTextEvent.new())
|
||||
|
||||
|
||||
func has_subsystem(_name:String) -> bool:
|
||||
return has_node(_name)
|
||||
|
||||
|
||||
func get_subsystem(_name:String) -> Variant:
|
||||
return get_node(_name)
|
||||
|
||||
|
||||
func add_subsytsem(_name:String, _script_path:String) -> Node:
|
||||
var node:Node = Node.new()
|
||||
node.name = _name
|
||||
node.set_script(load(_script_path))
|
||||
node.dialogic = self
|
||||
add_child(node)
|
||||
return node
|
||||
|
||||
|
||||
func _get(property):
|
||||
if has_subsystem(property):
|
||||
return get_node(str(property))
|
||||
|
||||
|
||||
func _set(property, value):
|
||||
if has_subsystem(property):
|
||||
return true
|
||||
|
||||
|
||||
################################################################################
|
||||
## PROCESSING FUNCTIONS
|
||||
################################################################################
|
||||
|
||||
func rebuild_character_directory() -> void:
|
||||
var characters: Array = DialogicUtil.list_resources_of_type(".dch")
|
||||
|
||||
# First sort by length of path, so shorter paths are first
|
||||
characters.sort_custom(func(a, b):return a.count("/") < b.count("/"))
|
||||
|
||||
# next we prepare the additional arrays needed for building the depth tree
|
||||
var shortened_paths:Array = []
|
||||
var reverse_array:Array = []
|
||||
var reverse_array_splits:Array = []
|
||||
|
||||
for i in characters.size():
|
||||
characters[i] = characters[i].replace("res:///", "res://")
|
||||
var path = characters[i].replace("res://","").replace(".dch", "")
|
||||
if path[0] == "/":
|
||||
path = path.right(-1)
|
||||
shortened_paths.append(path)
|
||||
|
||||
#split the shortened path up, and reverse it
|
||||
var path_breakdown = path.split("/")
|
||||
path_breakdown.reverse()
|
||||
|
||||
#Add the name of the file at beginning now, and another array saving the reversed split within each element
|
||||
reverse_array.append(path_breakdown[0])
|
||||
reverse_array_splits.append(path_breakdown)
|
||||
|
||||
|
||||
# Now the three arrays are prepped, begin the depth search
|
||||
var clean_search_path:bool = false
|
||||
var depth := 1
|
||||
|
||||
while !clean_search_path:
|
||||
var interim_array:Array = []
|
||||
clean_search_path = true
|
||||
|
||||
for i in shortened_paths.size():
|
||||
if reverse_array.count(reverse_array[i]) > 1:
|
||||
clean_search_path = false
|
||||
if depth < reverse_array_splits[i].size():
|
||||
interim_array.append(reverse_array_splits[i][depth] + "/" + reverse_array[i])
|
||||
else:
|
||||
interim_array.append(reverse_array[i])
|
||||
else:
|
||||
interim_array.append(reverse_array[i])
|
||||
depth += 1
|
||||
reverse_array = interim_array
|
||||
|
||||
# Now finally build the database from those arrays
|
||||
for i in characters.size():
|
||||
var entry:Dictionary = {}
|
||||
var charfile: DialogicCharacter= load(characters[i])
|
||||
entry['resource'] = charfile
|
||||
entry['full_path'] = characters[i]
|
||||
entry['unique_short_path'] = reverse_array[i]
|
||||
character_directory[reverse_array[i]] = entry
|
||||
|
||||
|
||||
func rebuild_timeline_directory() -> void:
|
||||
var characters: Array = DialogicUtil.list_resources_of_type(".dtl")
|
||||
|
||||
# First sort by length of path, so shorter paths are first
|
||||
characters.sort_custom(func(a, b):return a.count("/") < b.count("/"))
|
||||
|
||||
# next we prepare the additional arrays needed for building the depth tree
|
||||
var shortened_paths:Array = []
|
||||
var reverse_array:Array = []
|
||||
var reverse_array_splits:Array = []
|
||||
|
||||
for i in characters.size():
|
||||
characters[i] = characters[i].replace("res:///", "res://")
|
||||
var path = characters[i].replace("res://","").replace(".dtl", "")
|
||||
if path[0] == "/":
|
||||
path = path.right(-1)
|
||||
shortened_paths.append(path)
|
||||
|
||||
#split the shortened path up, and reverse it
|
||||
var path_breakdown = path.split("/")
|
||||
path_breakdown.reverse()
|
||||
|
||||
#Add the name of the file at beginning now, and another array saving the reversed split within each element
|
||||
reverse_array.append(path_breakdown[0])
|
||||
reverse_array_splits.append(path_breakdown)
|
||||
|
||||
|
||||
# Now the three arrays are prepped, begin the depth search
|
||||
var clean_search_path:bool = false
|
||||
var depth = 1
|
||||
|
||||
|
||||
while !clean_search_path:
|
||||
var interim_array:Array = []
|
||||
clean_search_path = true
|
||||
|
||||
for i in shortened_paths.size():
|
||||
if reverse_array.count(reverse_array[i]) > 1:
|
||||
clean_search_path = false
|
||||
if depth < reverse_array_splits[i].size():
|
||||
interim_array.append(reverse_array_splits[i][depth] + "/" + reverse_array[i])
|
||||
else:
|
||||
interim_array.append(reverse_array[i])
|
||||
else:
|
||||
interim_array.append(reverse_array[i])
|
||||
depth += 1
|
||||
reverse_array = interim_array
|
||||
|
||||
|
||||
|
||||
|
||||
# Now finally build the database from those arrays
|
||||
for i in characters.size():
|
||||
timeline_directory[reverse_array[i]] = characters[i]
|
||||
|
||||
|
||||
func find_timeline(path: String) -> String:
|
||||
if path in timeline_directory.keys():
|
||||
return timeline_directory[path]
|
||||
else:
|
||||
for i in timeline_directory.keys():
|
||||
if timeline_directory[i].contains(path):
|
||||
return timeline_directory[i]
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
func process_timeline(timeline: DialogicTimeline) -> DialogicTimeline:
|
||||
if timeline != null:
|
||||
if timeline.events_processed:
|
||||
return timeline
|
||||
else:
|
||||
#print(str(Time.get_ticks_msec()) + ": Starting process unloaded timeline")
|
||||
var end_event := DialogicEndBranchEvent.new()
|
||||
|
||||
var prev_indent := ""
|
||||
var events := []
|
||||
|
||||
# this is needed to add a end branch event even to empty conditions/choices
|
||||
var prev_was_opener := false
|
||||
|
||||
var lines := timeline.events
|
||||
var idx := -1
|
||||
var empty_lines = 0
|
||||
while idx < len(lines)-1:
|
||||
idx += 1
|
||||
|
||||
## First manage indent and creation of end branch events
|
||||
## For this, we still treat the event as a string
|
||||
var line: String = ""
|
||||
if typeof(lines[idx]) == TYPE_STRING:
|
||||
line = lines[idx]
|
||||
else:
|
||||
line = lines[idx]['event_node_as_text']
|
||||
|
||||
var line_stripped :String = line.strip_edges(true, false)
|
||||
if line_stripped.is_empty():
|
||||
empty_lines += 1
|
||||
continue
|
||||
|
||||
var indent :String= line.substr(0,len(line)-len(line_stripped))
|
||||
if len(indent) < len(prev_indent):
|
||||
for i in range(len(prev_indent)-len(indent)):
|
||||
events.append(end_event.duplicate())
|
||||
|
||||
elif prev_was_opener and len(indent) == len(prev_indent):
|
||||
events.append(end_event.duplicate())
|
||||
prev_indent = indent
|
||||
|
||||
## Now we process the event into a resource
|
||||
## by checking on each event if it recognizes this string
|
||||
var event_content :String = line_stripped
|
||||
var event :Variant
|
||||
for i in _event_script_cache:
|
||||
if i._test_event_string(event_content):
|
||||
event = i.duplicate()
|
||||
break
|
||||
|
||||
event.empty_lines_above = empty_lines
|
||||
# add the following lines until the event says it's full there is an empty line or the indent changes
|
||||
while !event.is_string_full_event(event_content):
|
||||
idx += 1
|
||||
if idx == len(lines):
|
||||
break
|
||||
var following_line :String = lines[idx]
|
||||
var following_line_stripped :String = following_line.strip_edges(true, false)
|
||||
var following_line_indent :String = following_line.substr(0,len(following_line)-len(following_line_stripped))
|
||||
if following_line_stripped.is_empty():
|
||||
break
|
||||
if following_line_indent != indent:
|
||||
idx -= 1
|
||||
break
|
||||
event_content += "\n"+following_line_stripped
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
# Unlike at runtime, for some reason here the event scripts can't access the scene tree to get to the character directory, so we will need to pass it to it before processing
|
||||
if event['event_name'] == 'Character' || event['event_name'] == 'Text':
|
||||
event.set_meta('editor_character_directory', character_directory)
|
||||
|
||||
|
||||
event._load_from_string(event_content)
|
||||
event['event_node_as_text'] = event_content
|
||||
|
||||
events.append(event)
|
||||
prev_was_opener = event.can_contain_events
|
||||
empty_lines = 0
|
||||
|
||||
|
||||
if !prev_indent.is_empty():
|
||||
for i in range(len(prev_indent)):
|
||||
events.append(end_event.duplicate())
|
||||
|
||||
timeline.events = events
|
||||
timeline.events_processed = true
|
||||
#print(str(Time.get_ticks_msec()) + ": Finished process unloaded timeline")
|
||||
return timeline
|
||||
else:
|
||||
return DialogicTimeline.new()
|
||||
|
||||
|
||||
################################################################################
|
||||
## FOR END USER
|
||||
################################################################################
|
||||
func start(timeline, single_instance = true):
|
||||
var dialog_scene_path: String = DialogicUtil.get_project_setting(
|
||||
'dialogic/editor/default_dialog_scene', "res://addons/dialogic/Example Assets/example-scenes/DialogicDefaultScene.tscn")
|
||||
if single_instance:
|
||||
if get_tree().get_nodes_in_group('dialogic_main_node').is_empty():
|
||||
var scene = load(dialog_scene_path).instantiate()
|
||||
get_parent().call_deferred("add_child", scene)
|
||||
Dialogic.start_timeline(timeline)
|
||||
|
||||
func is_running() -> bool:
|
||||
if get_tree().get_nodes_in_group('dialogic_main_node').is_empty():
|
||||
return false
|
||||
return true
|
||||
184
Sprout/addons/dialogic/Other/DialogicUtil.gd
Normal file
184
Sprout/addons/dialogic/Other/DialogicUtil.gd
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
@tool
|
||||
class_name DialogicUtil
|
||||
|
||||
static func get_editor_scale() -> float:
|
||||
return get_dialogic_plugin().editor_interface.get_editor_scale()
|
||||
|
||||
|
||||
static func listdir(path: String, files_only: bool = true, throw_error:bool = true, full_file_path:bool = false) -> Array:
|
||||
var files: Array = []
|
||||
if DirAccess.dir_exists_absolute(path):
|
||||
var dir := DirAccess.open(path)
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
while file_name != "":
|
||||
if not file_name.begins_with("."):
|
||||
if files_only:
|
||||
if not dir.current_is_dir() and not file_name.ends_with('.import'):
|
||||
if full_file_path:
|
||||
files.append(path.path_join(file_name))
|
||||
else:
|
||||
files.append(file_name)
|
||||
else:
|
||||
if full_file_path:
|
||||
files.append(path.path_join(file_name))
|
||||
else:
|
||||
files.append(file_name)
|
||||
file_name = dir.get_next()
|
||||
dir.list_dir_end()
|
||||
return files
|
||||
|
||||
|
||||
static func get_dialogic_plugin() -> Node:
|
||||
var tree: SceneTree = Engine.get_main_loop()
|
||||
if tree.get_root().get_child(0).has_node('DialogicPlugin'):
|
||||
return tree.get_root().get_child(0).get_node('DialogicPlugin')
|
||||
return null
|
||||
|
||||
|
||||
static func list_resources_of_type(extension:String) -> Array:
|
||||
var all_resources := scan_folder('res://', extension)
|
||||
return all_resources
|
||||
|
||||
|
||||
static func scan_folder(path:String, extension:String) -> Array:
|
||||
var list: Array = []
|
||||
if DirAccess.dir_exists_absolute(path):
|
||||
var dir := DirAccess.open(path)
|
||||
dir.list_dir_begin()
|
||||
var file_name := dir.get_next()
|
||||
while file_name != "":
|
||||
if dir.current_is_dir() and not file_name.begins_with("."):
|
||||
list += scan_folder(path.path_join(file_name), extension)
|
||||
else:
|
||||
if file_name.ends_with(extension):
|
||||
list.append(path.path_join(file_name))
|
||||
file_name = dir.get_next()
|
||||
return list
|
||||
|
||||
|
||||
static func guess_resource(extension, identifier):
|
||||
var resources = list_resources_of_type(extension)
|
||||
for resource_path in resources:
|
||||
if resource_path.get_file().trim_suffix(extension) == identifier:
|
||||
return resource_path
|
||||
|
||||
#
|
||||
#static func get_event_by_string(string:String) -> Resource:
|
||||
# var event_scripts = get_event_scripts()
|
||||
#
|
||||
# # move the text event to the end of the list as it's the default.
|
||||
# event_scripts.erase("res://addons/dialogic/Events/Text/event_text.gd")
|
||||
# event_scripts.append("res://addons/dialogic/Events/Text/event_text.gd")
|
||||
#
|
||||
# for event in event_scripts:
|
||||
# if load(event).new()._test_event_string(string):
|
||||
#
|
||||
# return load(event)
|
||||
# return load("res://addons/dialogic/Events/Text/event_text.gd")
|
||||
|
||||
|
||||
static func get_project_setting(setting:String, default = null):
|
||||
return ProjectSettings.get_setting(setting) if ProjectSettings.has_setting(setting) else default
|
||||
|
||||
|
||||
static func get_indexers(include_custom := true, force_reload := false) -> Array[DialogicIndexer]:
|
||||
if Engine.get_main_loop().has_meta('dialogic_indexers') and !force_reload:
|
||||
return Engine.get_main_loop().get_meta('dialogic_indexers')
|
||||
|
||||
var indexers := []
|
||||
|
||||
for file in listdir("res://addons/dialogic/Events/", false):
|
||||
var possible_script:String = "res://addons/dialogic/Events/" + file + "/index.gd"
|
||||
if FileAccess.file_exists(possible_script):
|
||||
indexers.append(load(possible_script).new())
|
||||
|
||||
if include_custom:
|
||||
for file in listdir("res://addons/dialogic_additions/Events/", false, false):
|
||||
var possible_script: String = "res://addons/dialogic_additions/Events/" + file + "/index.gd"
|
||||
if FileAccess.file_exists(possible_script):
|
||||
indexers.append(load(possible_script).new())
|
||||
|
||||
Engine.get_main_loop().set_meta('dialogic_indexers', indexers)
|
||||
return indexers
|
||||
|
||||
|
||||
static func pretty_name(script:String) -> String:
|
||||
var _name = script.get_file().trim_suffix("."+script.get_extension())
|
||||
_name = _name.replace('_', ' ')
|
||||
_name = _name.capitalize()
|
||||
return _name
|
||||
|
||||
|
||||
static func str_to_bool(boolstring:String) -> bool:
|
||||
return true if boolstring == "true" else false
|
||||
|
||||
|
||||
static func logical_convert(value):
|
||||
if typeof(value) == TYPE_STRING:
|
||||
if value.is_valid_int():
|
||||
return value.to_int()
|
||||
if value.is_valid_float():
|
||||
return value.to_float()
|
||||
if value == 'true':
|
||||
return true
|
||||
if value == 'false':
|
||||
return false
|
||||
return value
|
||||
|
||||
|
||||
static func get_color_palette(default:bool = false) -> Dictionary:
|
||||
# Colors are using the ProjectSettings instead of the EditorSettings
|
||||
# because there is a bug in Godot which prevents us from using it.
|
||||
# When you try to do it, the text in the timelines goes to weird artifacts
|
||||
# and on the Output panel you see the error:
|
||||
# ./core/rid.h:151 - Condition "!id_map.has(p_rid.get_data())" is true. Returned: nullptr
|
||||
# over and over again.
|
||||
# Might revisit this in Godot 4, but don't have any high hopes for it improving.
|
||||
var colors = [
|
||||
Color('#3b8bf2'), # Blue
|
||||
Color('#00b15f'), # Green
|
||||
Color('#9468e8'), # Purple
|
||||
Color('#de5c5c'), # Red
|
||||
Color('#fa952a'), # Orange
|
||||
Color('#7C7C7C') # Gray
|
||||
]
|
||||
var color_dict = {}
|
||||
var index = 1
|
||||
for n in colors:
|
||||
var color_name = 'Color' + str(index)
|
||||
color_dict[color_name] = n
|
||||
if !default:
|
||||
if ProjectSettings.has_setting('dialogic/editor/' + color_name):
|
||||
color_dict[color_name] = ProjectSettings.get_setting('dialogic/editor/' + color_name)
|
||||
index += 1
|
||||
|
||||
return color_dict
|
||||
|
||||
|
||||
static func get_color(value:String) -> Color:
|
||||
var colors = get_color_palette()
|
||||
return colors[value]
|
||||
|
||||
|
||||
static func is_physics_timer()->bool:
|
||||
return get_project_setting('dialogic/timer/process_in_physics', false)
|
||||
|
||||
|
||||
static func update_timer_process_callback(timer:Timer) -> void:
|
||||
timer.process_callback = Timer.TIMER_PROCESS_PHYSICS if is_physics_timer() else Timer.TIMER_PROCESS_IDLE
|
||||
|
||||
static func get_next_translation_id() -> String:
|
||||
ProjectSettings.set_setting('dialogic/translation/id_counter', get_project_setting('dialogic/translation/id_counter', 16)+1)
|
||||
return '%x' % get_project_setting('dialogic/translation/id_counter', 16)
|
||||
|
||||
|
||||
# helper that converts a nested variable dictionary into an array with paths
|
||||
static func list_variables(dict:Dictionary, path := "") -> Array:
|
||||
var array := []
|
||||
for key in dict.keys():
|
||||
if typeof(dict[key]) == TYPE_DICTIONARY:
|
||||
array.append_array(list_variables(dict[key], path+key+"."))
|
||||
else:
|
||||
array.append(path+key)
|
||||
return array
|
||||
15
Sprout/addons/dialogic/Other/Dialogic_Subsystem.gd
Normal file
15
Sprout/addons/dialogic/Other/Dialogic_Subsystem.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
extends Node
|
||||
class_name DialogicSubsystem
|
||||
|
||||
var dialogic = null
|
||||
|
||||
# To be overriden by sub-classes
|
||||
# Fill in everything that should be cleared (for example before loading a different state)
|
||||
func clear_game_state():
|
||||
pass
|
||||
|
||||
# To be overriden by sub-classes
|
||||
# Fill in everything that should be loaded using the dialogic_game_handler.current_state_info
|
||||
# This is called when a save is loaded
|
||||
func load_game_state():
|
||||
pass
|
||||
27
Sprout/addons/dialogic/Other/index_class.gd
Normal file
27
Sprout/addons/dialogic/Other/index_class.gd
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@tool
|
||||
class_name DialogicIndexer
|
||||
extends RefCounted
|
||||
|
||||
## Script that indexes events, subsystems, settings pages and more.
|
||||
## Place a script of this type in every folder in "addons/Events".
|
||||
## Overwrite the methods to return the contents of that folder.
|
||||
|
||||
|
||||
var this_folder : String = get_script().resource_path.get_base_dir()
|
||||
|
||||
func _get_events() -> Array:
|
||||
if FileAccess.file_exists(this_folder.path_join('event.gd')):
|
||||
return [this_folder.path_join('event.gd')]
|
||||
return []
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return []
|
||||
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return []
|
||||
|
||||
|
||||
func _get_character_editor_tabs() -> Array:
|
||||
return []
|
||||
Loading…
Add table
Add a link
Reference in a new issue