123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- tool
- extends HSplitContainer
-
- var editor_reference
- var timeline_name: String = ''
- var timeline_file: String = ''
- var current_timeline: Dictionary = {}
-
- onready var master_tree = get_node('../MasterTreeContainer/MasterTree')
- onready var timeline = $TimelineArea/TimeLine
- onready var events_warning = $ScrollContainer/EventContainer/EventsWarning
-
- var hovered_item = null
- var selected_style : StyleBoxFlat = load("res://addons/dialogic/Editor/Pieces/selected_styleboxflat.tres")
- var selected_style_text : StyleBoxFlat = load("res://addons/dialogic/Editor/Pieces/selected_styleboxflat_text_event.tres")
- var saved_style : StyleBoxFlat
- var selected_item : Node
-
-
- var moving_piece = null
- var piece_was_dragged = false
-
- func _ready():
- var modifier = ''
- var _scale = get_constant("inspector_margin", "Editor")
- _scale = _scale * 0.125
- $ScrollContainer.rect_min_size.x = 180
- if _scale == 1.25:
- modifier = '-1.25'
- $ScrollContainer.rect_min_size.x = 200
- if _scale == 1.5:
- modifier = '-1.25'
- $ScrollContainer.rect_min_size.x = 200
- if _scale == 1.75:
- modifier = '-1.25'
- $ScrollContainer.rect_min_size.x = 390
- if _scale == 2:
- modifier = '-2'
- $ScrollContainer.rect_min_size.x = 390
-
- # We connect all the event buttons to the event creation functions
- for b in $ScrollContainer/EventContainer.get_children():
- if b is Button:
- if b.name == 'ButtonQuestion':
- b.connect('pressed', self, "_on_ButtonQuestion_pressed", [])
- elif b.name == 'IfCondition':
- b.connect('pressed', self, "_on_ButtonCondition_pressed", [])
- else:
- b.connect('pressed', self, "_create_event_button_pressed", [b.name])
-
- var style = $TimelineArea.get('custom_styles/bg')
- style.set('bg_color', get_color("dark_color_1", "Editor"))
-
-
- func delete_event():
- # get next element
- var next = min(timeline.get_child_count() - 1, selected_item.get_index() + 1)
- var next_node = timeline.get_child(next)
- if (next_node == selected_item):
- next_node = null
-
- # remove current
- selected_item.get_parent().remove_child(selected_item)
- selected_item.queue_free()
- selected_item = null
-
- # select next
- if (next_node != null):
- _select_item(next_node)
- else:
- if (timeline.get_child_count() > 0):
- next_node = timeline.get_child(max(0, timeline.get_child_count() - 1))
- if (next_node != null):
- _select_item(next_node)
-
- indent_events()
-
- func _input(event):
- # some shortcuts need to get handled in the common input event
- # especially CTRL-based
- # because certain godot controls swallow events (like textedit)
- # we protect this with is_visible_in_tree to not
- # invoke a shortcut by accident
- if (event is InputEventKey and event is InputEventWithModifiers and is_visible_in_tree()):
- # CTRL UP
- if (event.pressed
- and event.alt == false
- and event.shift == false
- and event.control == true
- and event.scancode == KEY_UP
- and event.echo == false
- ):
- # select previous
- if (selected_item != null):
- var prev = max(0, selected_item.get_index() - 1)
- var prev_node = timeline.get_child(prev)
- if (prev_node != selected_item):
- _select_item(prev_node)
- get_tree().set_input_as_handled()
-
- pass
-
- # CTRL DOWN
- if (event.pressed
- and event.alt == false
- and event.shift == false
- and event.control == true
- and event.scancode == KEY_DOWN
- and event.echo == false
- ):
- # select next
- if (selected_item != null):
- var next = min(timeline.get_child_count() - 1, selected_item.get_index() + 1)
- var next_node = timeline.get_child(next)
- if (next_node != selected_item):
- _select_item(next_node)
- get_tree().set_input_as_handled()
-
- pass
-
- # CTRL DELETE
- if (event.pressed
- and event.alt == false
- and event.shift == false
- and event.control == true
- and event.scancode == KEY_DELETE
- and event.echo == false
- ):
- if (selected_item != null):
- delete_event()
- get_tree().set_input_as_handled()
- pass
-
- # CTRL T
- if (event.pressed
- and event.alt == false
- and event.shift == false
- and event.control == true
- and event.scancode == KEY_T
- and event.echo == false
- ):
- var new_text = create_event("TextBlock")
- _select_item(new_text)
- indent_events()
- get_tree().set_input_as_handled()
- pass
-
-
- func _unhandled_key_input(event):
- if (event is InputEventWithModifiers):
- # ALT UP
- if (event.pressed
- and event.alt == true
- and event.shift == false
- and event.control == false
- and event.scancode == KEY_UP
- and event.echo == false
- ):
- # move selected up
- if (selected_item != null):
- move_block(selected_item, "up")
- indent_events()
- get_tree().set_input_as_handled()
-
- pass
-
- # ALT DOWN
- if (event.pressed
- and event.alt == true
- and event.shift == false
- and event.control == false
- and event.scancode == KEY_DOWN
- and event.echo == false
- ):
- # move selected down
- if (selected_item != null):
- move_block(selected_item, "down")
- indent_events()
- get_tree().set_input_as_handled()
-
- pass
-
- pass
-
- func _process(delta):
- if moving_piece != null:
- var current_position = get_global_mouse_position()
- var node_position = moving_piece.rect_global_position.y
- var height = get_block_height(moving_piece)
- var up_offset = get_block_height(get_block_above(moving_piece))
- var down_offset = get_block_height(get_block_below(moving_piece))
- if up_offset != null:
- up_offset = (up_offset / 2) + 5
- if current_position.y < node_position - up_offset:
- move_block(moving_piece, 'up')
- piece_was_dragged = true
- if down_offset != null:
- down_offset = height + (down_offset / 2) + 5
- if current_position.y > node_position + down_offset:
- move_block(moving_piece, 'down')
- piece_was_dragged = true
-
-
- func _clear_selection():
- if selected_item != null and saved_style != null:
- var selected_panel: PanelContainer = selected_item.get_node("PanelContainer")
- if selected_panel != null:
- selected_panel.set('custom_styles/panel', saved_style)
-
- selected_item = null
- saved_style = null
-
-
- func _is_item_selected(item: Node):
- return item == selected_item
-
-
- func _select_item(item: Node):
- if item != null and not _is_item_selected(item):
- _clear_selection()
- var panel: PanelContainer = item.get_node("PanelContainer")
- if panel != null:
- saved_style = panel.get('custom_styles/panel')
- selected_item = item
- if selected_item.event_data.has('text') and selected_item.event_data.has('character'):
- panel.set('custom_styles/panel', selected_style_text)
- else:
- panel.set('custom_styles/panel', selected_style)
- # allow event panels to do additional operation when getting selected
- if (selected_item.has_method("on_timeline_selected")):
- selected_item.on_timeline_selected()
- else:
- _clear_selection()
-
-
- func _on_gui_input(event, item: Node):
- if event is InputEventMouseButton and event.button_index == 1:
- if (not event.is_pressed()):
- if (not piece_was_dragged and moving_piece != null):
- _clear_selection()
- if (moving_piece != null):
- indent_events()
- moving_piece = null
- elif event.is_pressed():
- moving_piece = item
- if not _is_item_selected(item):
- _select_item(item)
- piece_was_dragged = true
- else:
- piece_was_dragged = false
-
-
- # Event Creation signal for buttons
- func _create_event_button_pressed(button_name):
- create_event(button_name)
- indent_events()
-
-
- func _on_ButtonQuestion_pressed() -> void:
- if selected_item != null:
- # Events are added bellow the selected node
- # So we must reverse the adding order
- create_event("EndBranch", {'no-data': true}, true)
- create_event("Choice", {'no-data': true}, true)
- create_event("Choice", {'no-data': true}, true)
- create_event("Question", {'no-data': true}, true)
- else:
- create_event("Question", {'no-data': true}, true)
- create_event("Choice", {'no-data': true}, true)
- create_event("Choice", {'no-data': true}, true)
- create_event("EndBranch", {'no-data': true}, true)
-
-
- func _on_ButtonCondition_pressed() -> void:
- if selected_item != null:
- # Events are added bellow the selected node
- # So we must reverse the adding order
- create_event("EndBranch", {'no-data': true}, true)
- create_event("IfCondition", {'no-data': true}, true)
- else:
- create_event("IfCondition", {'no-data': true}, true)
- create_event("EndBranch", {'no-data': true}, true)
-
-
- # Adding an event to the timeline
- func create_event(scene: String, data: Dictionary = {'no-data': true} , indent: bool = false):
- # This function will create an event in the timeline.
- var piece = load("res://addons/dialogic/Editor/Pieces/" + scene + ".tscn").instance()
- piece.editor_reference = editor_reference
- if selected_item != null:
- timeline.add_child_below_node(selected_item, piece)
- else:
- timeline.add_child(piece)
- if data.has('no-data') == false:
- piece.load_data(data)
-
- piece.connect("gui_input", self, '_on_gui_input', [piece])
- events_warning.visible = false
- # Indent on create
- if indent:
- indent_events()
- return piece
-
-
- # Event Indenting
- func indent_events() -> void:
- var indent: int = 0
- var starter: bool = false
- var event_list: Array = timeline.get_children()
- var question_index: int = 0
- var question_indent = {}
- if event_list.size() < 2:
- return
- # Resetting all the indents
- for event in event_list:
- var indent_node = event.get_node("Indent")
- indent_node.visible = false
-
- # Adding new indents
- for event in event_list:
- # since there are indicators now, not all elements
- # in this list have an event_data property
- if (not "event_data" in event):
- continue
-
- if event.event_data.has('question') or event.event_data.has('condition'):
- indent += 1
- starter = true
- question_index += 1
- question_indent[question_index] = indent
- if event.event_data.has('choice'):
- if question_index > 0:
- indent = question_indent[question_index] + 1
- starter = true
- if event.event_data.has('endbranch'):
- if question_indent.has(question_index):
- indent = question_indent[question_index]
- indent -= 1
- question_index -= 1
- if indent < 0:
- indent = 0
-
- if indent > 0:
- var indent_node = event.get_node("Indent")
- indent_node.rect_min_size = Vector2(25 * indent, 0)
- indent_node.visible = true
- if starter:
- indent_node.rect_min_size = Vector2(25 * (indent - 1), 0)
- if indent - 1 == 0:
- indent_node.visible = false
-
- starter = false
-
-
- func load_timeline(filename: String):
- #print('---------------------------')
- #print('Loading: ', filename)
- clear_timeline()
- var start_time = OS.get_system_time_msecs()
- timeline_file = filename
-
- var data = DialogicResources.get_timeline_json(filename)
- if data['metadata'].has('name'):
- timeline_name = data['metadata']['name']
- else:
- timeline_name = data['metadata']['file']
- data = data['events']
- for i in data:
- match i:
- {'text', 'character', 'portrait'}:
- create_event("TextBlock", i)
- {'background'}:
- create_event("SceneEvent", i)
- {'character', 'action', 'position', 'portrait'}:
- create_event("CharacterJoinBlock", i)
- {'audio', 'file'}:
- create_event("AudioBlock", i)
- {'background-music', 'file'}:
- create_event("BackgroundMusic", i)
- {'question', 'options'}:
- create_event("Question", i)
- {'choice'}:
- create_event("Choice", i)
- {'endbranch'}:
- create_event("EndBranch", i)
- {'character', 'action'}:
- create_event("CharacterLeaveBlock", i)
- {'change_timeline'}:
- create_event("ChangeTimeline", i)
- {'emit_signal'}:
- create_event("EmitSignal", i)
- {'change_scene'}:
- create_event("ChangeScene", i)
- {'close_dialog'}:
- create_event("CloseDialog", i)
- {'wait_seconds'}:
- create_event("WaitSeconds", i)
- {'condition', 'definition', 'value'}:
- create_event("IfCondition", i)
- {'set_value', 'definition', ..}:
- create_event("SetValue", i)
- {'set_theme'}:
- create_event("SetTheme", i)
- {'call_node'}:
- create_event("CallNode", i)
-
- if data.size() < 1:
- events_warning.visible = true
- else:
- events_warning.visible = false
- indent_events()
- #fold_all_nodes()
-
- var elapsed_time = (OS.get_system_time_msecs() - start_time) * 0.001
- #editor_reference.dprint("Loading time: " + str(elapsed_time))
-
-
- func clear_timeline():
- _clear_selection()
- for event in timeline.get_children():
- event.free()
-
-
- func get_block_above(block):
- var block_index = block.get_index()
- var item = null
- if block_index > 0:
- item = timeline.get_child(block_index - 1)
- return item
-
-
- func get_block_below(block):
- var block_index = block.get_index()
- var item = null
- if block_index < timeline.get_child_count() - 1:
- item = timeline.get_child(block_index + 1)
- return item
-
-
- func get_block_height(block):
- if block != null:
- return block.get_node("PanelContainer").rect_size.y
- else:
- return null
-
-
- # ordering blocks in timeline
- func move_block(block, direction):
- var block_index = block.get_index()
- if direction == 'up':
- if block_index > 0:
- timeline.move_child(block, block_index - 1)
- return true
- if direction == 'down':
- timeline.move_child(block, block_index + 1)
- return true
- return false
-
-
- func create_timeline():
- timeline_file = 'timeline-' + str(OS.get_unix_time()) + '.json'
- var timeline = {
- "events": [],
- "metadata":{
- "dialogic-version": editor_reference.version_string,
- "file": timeline_file
- }
- }
- DialogicResources.set_timeline(timeline)
- return timeline
-
-
- func new_timeline():
- # This event creates and selects the new timeline
- master_tree.build_timelines(create_timeline()['metadata']['file'])
-
-
- # Saving
- func generate_save_data():
- var info_to_save = {
- 'metadata': {
- 'dialogic-version': editor_reference.version_string,
- 'name': timeline_name,
- 'file': timeline_file
- },
- 'events': []
- }
- for event in timeline.get_children():
- # check that event has event_data (e.g. drag drop indicators)
- if (not "event_data" in event):
- continue
- if event.is_queued_for_deletion() == false: # Checking that the event is not waiting to be removed
- info_to_save['events'].append(event.event_data)
- return info_to_save
-
-
- func save_timeline() -> void:
- if timeline_file != '':
- var info_to_save = generate_save_data()
- DialogicResources.set_timeline(info_to_save)
- #print('[+] Saving: ' , timeline_file)
-
-
- # Utilities
- func fold_all_nodes():
- for event in timeline.get_children():
- if event.has_node("PanelContainer/VBoxContainer/Header/VisibleToggle"):
- event.get_node("PanelContainer/VBoxContainer/Header/VisibleToggle").set_pressed(false)
-
-
- func unfold_all_nodes():
- for event in timeline.get_children():
- if event.has_node("PanelContainer/VBoxContainer/Header/VisibleToggle"):
- event.get_node("PanelContainer/VBoxContainer/Header/VisibleToggle").set_pressed(true)
|