diff --git a/addons/dialogic/Modules/Text/event_text.gd b/addons/dialogic/Modules/Text/event_text.gd index 7e29d4a..77b165e 100644 --- a/addons/dialogic/Modules/Text/event_text.gd +++ b/addons/dialogic/Modules/Text/event_text.gd @@ -24,18 +24,18 @@ var portrait := "" ## Used to set the character resource from the unique name identifier and vice versa var character_identifier: String: - get: - if character and not "{" in character_identifier: - var identifier := character.get_identifier() - if not identifier.is_empty(): - return identifier - return character_identifier - set(value): - character_identifier = value - character = DialogicResourceUtil.get_character_resource(value) - if Engine.is_editor_hint() and ((not character) or (character and not character.portraits.has(portrait))): - portrait = "" - ui_update_needed.emit() + get: + if character and not "{" in character_identifier: + var identifier := character.get_identifier() + if not identifier.is_empty(): + return identifier + return character_identifier + set(value): + character_identifier = value + character = DialogicResourceUtil.get_character_resource(value) + if Engine.is_editor_hint() and ((not character) or (character and not character.portraits.has(portrait))): + portrait = "" + ui_update_needed.emit() var regex := RegEx.create_from_string(r'\s*((")?(?(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*(?\(.*\)))?\s*(?(.|\n)*)') var split_regex := RegEx.create_from_string(r"((\[n\]|\[n\+\])?((?!(\[n\]|\[n\+\]))(.|\n))+)") @@ -49,225 +49,225 @@ signal advance ################################################################################ func _clear_state() -> void: - dialogic.current_state_info.erase('text_sub_idx') - _disconnect_signals() + dialogic.current_state_info.erase('text_sub_idx') + _disconnect_signals() func _execute() -> void: - if text.is_empty(): - finish() - return + if text.is_empty(): + finish() + return - ## If the speaker is provided as an expression, parse it now. - if "{" in character_identifier: - character = null - var character_name: String = dialogic.Expressions.execute_string(character_identifier) - get_or_create_character(character_name) + ## If the speaker is provided as an expression, parse it now. + if "{" in character_identifier: + character = null + var character_name: String = dialogic.Expressions.execute_string(character_identifier) + get_or_create_character(character_name) - ## Change Portrait and Active Speaker - if dialogic.has_subsystem("Portraits"): - if character: + ## Change Portrait and Active Speaker + if dialogic.has_subsystem("Portraits"): + if character: - dialogic.Portraits.change_speaker(character, portrait) + dialogic.Portraits.change_speaker(character, portrait) - if portrait and dialogic.Portraits.is_character_joined(character): - dialogic.Portraits.change_character_portrait(character, portrait) + if portrait and dialogic.Portraits.is_character_joined(character): + dialogic.Portraits.change_character_portrait(character, portrait) - else: - dialogic.Portraits.change_speaker(null) + else: + dialogic.Portraits.change_speaker(null) - ## Change and Type Sound Mood - if character: - dialogic.Text.update_name_label(character) + ## Change and Type Sound Mood + if character: + dialogic.Text.update_name_label(character) - var current_portrait: String = portrait - if portrait.is_empty(): - current_portrait = dialogic.current_state_info["portraits"].get(character.get_identifier(), {}).get("portrait", "") + var current_portrait: String = portrait + if portrait.is_empty(): + current_portrait = dialogic.current_state_info["portraits"].get(character.get_identifier(), {}).get("portrait", "") - var current_portrait_sound_mood: String = character.portraits.get(current_portrait, {}).get("sound_mood", "") - dialogic.Text.update_typing_sound_mood_from_character(character, current_portrait_sound_mood) + var current_portrait_sound_mood: String = character.portraits.get(current_portrait, {}).get("sound_mood", "") + dialogic.Text.update_typing_sound_mood_from_character(character, current_portrait_sound_mood) - else: - dialogic.Text.update_name_label(null) - dialogic.Text.update_typing_sound_mood() + else: + dialogic.Text.update_name_label(null) + dialogic.Text.update_typing_sound_mood() - ## Handle style changes - if dialogic.has_subsystem("Styles"): - var current_base_style: String = dialogic.current_state_info.get("base_style") - var current_style: String = dialogic.current_state_info.get("style", "") - var character_style: String = "" if not character else character.custom_info.get("style", "") + ## Handle style changes + if dialogic.has_subsystem("Styles"): + var current_base_style: String = dialogic.current_state_info.get("base_style") + var current_style: String = dialogic.current_state_info.get("style", "") + var character_style: String = "" if not character else character.custom_info.get("style", "") - ## Change back to base style, if another characters style is currently used - if (not character or character_style.is_empty()) and (current_base_style != current_style): - dialogic.Styles.change_style(dialogic.current_state_info.get("base_style", "Default")) - await dialogic.get_tree().process_frame + ## Change back to base style, if another characters style is currently used + if (not character or character_style.is_empty()) and (current_base_style != current_style): + dialogic.Styles.change_style(dialogic.current_state_info.get("base_style", "Default")) + await dialogic.get_tree().process_frame - ## Change to the characters style if this character has one - elif character and not character_style.is_empty(): - dialogic.Styles.change_style(character_style, false) - await dialogic.get_tree().process_frame + ## Change to the characters style if this character has one + elif character and not character_style.is_empty(): + dialogic.Styles.change_style(character_style, false) + await dialogic.get_tree().process_frame - _connect_signals() + _connect_signals() - var character_name_text := dialogic.Text.get_character_name_parsed(character) - var final_text: String = get_property_translated('text') - if ProjectSettings.get_setting('dialogic/text/split_at_new_lines', false): - match ProjectSettings.get_setting('dialogic/text/split_at_new_lines_as', 0): - 0: - final_text = final_text.replace('\n', '[n]') - 1: - final_text = final_text.replace('\n', '[n+][br]') + var character_name_text := dialogic.Text.get_character_name_parsed(character) + var final_text: String = get_property_translated('text') + if ProjectSettings.get_setting('dialogic/text/split_at_new_lines', false): + match ProjectSettings.get_setting('dialogic/text/split_at_new_lines_as', 0): + 0: + final_text = final_text.replace('\n', '[n]') + 1: + final_text = final_text.replace('\n', '[n+][br]') - var split_text := [] - for i in split_regex.search_all(final_text): - split_text.append([i.get_string().trim_prefix('[n]').trim_prefix('[n+]')]) - split_text[-1].append(i.get_string().begins_with('[n+]')) + var split_text := [] + for i in split_regex.search_all(final_text): + split_text.append([i.get_string().trim_prefix('[n]').trim_prefix('[n+]')]) + split_text[-1].append(i.get_string().begins_with('[n+]')) - dialogic.current_state_info['text_sub_idx'] = dialogic.current_state_info.get('text_sub_idx', -1) + dialogic.current_state_info['text_sub_idx'] = dialogic.current_state_info.get('text_sub_idx', -1) - var reveal_next_segment: bool = dialogic.current_state_info['text_sub_idx'] == -1 + var reveal_next_segment: bool = dialogic.current_state_info['text_sub_idx'] == -1 - for section_idx in range(min(max(0, dialogic.current_state_info['text_sub_idx']), len(split_text)-1), len(split_text)): - dialogic.Inputs.block_input(ProjectSettings.get_setting('dialogic/text/text_reveal_skip_delay', 0.1)) + for section_idx in range(min(max(0, dialogic.current_state_info['text_sub_idx']), len(split_text)-1), len(split_text)): + dialogic.Inputs.block_input(ProjectSettings.get_setting('dialogic/text/text_reveal_skip_delay', 0.1)) - if reveal_next_segment: - dialogic.Text.hide_next_indicators() + if reveal_next_segment: + dialogic.Text.hide_next_indicators() - dialogic.current_state_info['text_sub_idx'] = section_idx + dialogic.current_state_info['text_sub_idx'] = section_idx - var segment: String = dialogic.Text.parse_text(split_text[section_idx][0], 0) - var is_append: bool = split_text[section_idx][1] + var segment: String = dialogic.Text.parse_text(split_text[section_idx][0], 0) + var is_append: bool = split_text[section_idx][1] - final_text = ProjectSettings.get_setting("dialogic/text/dialog_text_prefix", "")+segment - dialogic.Text.about_to_show_text.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append}) + final_text = ProjectSettings.get_setting("dialogic/text/dialog_text_prefix", "")+segment + dialogic.Text.about_to_show_text.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append}) - await dialogic.Text.update_textbox(final_text, false) + await dialogic.Text.update_textbox(final_text, false) - state = States.REVEALING - _try_play_current_line_voice() - final_text = dialogic.Text.update_dialog_text(final_text, false, is_append) + state = States.REVEALING + _try_play_current_line_voice() + final_text = dialogic.Text.update_dialog_text(final_text, false, is_append) - dialogic.Text.text_started.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append}) + dialogic.Text.text_started.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append}) - _mark_as_read(character_name_text, final_text) + _mark_as_read(character_name_text, final_text) - # We must skip text animation before we potentially return when there - # is a Choice event. - if dialogic.Inputs.auto_skip.enabled: - dialogic.Text.skip_text_reveal() - else: - await dialogic.Text.text_finished + # We must skip text animation before we potentially return when there + # is a Choice event. + if dialogic.Inputs.auto_skip.enabled: + dialogic.Text.skip_text_reveal() + else: + await dialogic.Text.text_finished - state = States.IDLE - else: - reveal_next_segment = true + state = States.IDLE + else: + reveal_next_segment = true - # Handling potential Choice Events. - if section_idx == len(split_text)-1 and dialogic.has_subsystem('Choices') and dialogic.Choices.is_question(dialogic.current_event_idx): - dialogic.Text.show_next_indicators(true) + # Handling potential Choice Events. + if section_idx == len(split_text)-1 and dialogic.has_subsystem('Choices') and dialogic.Choices.is_question(dialogic.current_event_idx): + dialogic.Text.show_next_indicators(true) - finish() - return + finish() + return - elif dialogic.Inputs.auto_advance.is_enabled(): - dialogic.Text.show_next_indicators(false, true) - dialogic.Inputs.auto_advance.start() - else: - dialogic.Text.show_next_indicators() + elif dialogic.Inputs.auto_advance.is_enabled(): + dialogic.Text.show_next_indicators(false, true) + dialogic.Inputs.auto_advance.start() + else: + dialogic.Text.show_next_indicators() - if section_idx == len(split_text)-1: - state = States.DONE + if section_idx == len(split_text)-1: + state = States.DONE - # If Auto-Skip is enabled and there are multiple parts of this text - # we need to skip the text after the defined time per event. - if dialogic.Inputs.auto_skip.enabled: - await dialogic.Inputs.start_autoskip_timer() + # If Auto-Skip is enabled and there are multiple parts of this text + # we need to skip the text after the defined time per event. + if dialogic.Inputs.auto_skip.enabled: + await dialogic.Inputs.start_autoskip_timer() - # Check if Auto-Skip is still enabled. - if not dialogic.Inputs.auto_skip.enabled: - await advance + # Check if Auto-Skip is still enabled. + if not dialogic.Inputs.auto_skip.enabled: + await advance - else: - await advance + else: + await advance - finish() + finish() func _mark_as_read(character_name_text: String, final_text: String) -> void: - if dialogic.has_subsystem('History'): - if character: - dialogic.History.store_simple_history_entry(final_text, event_name, {'character':character_name_text, 'character_color':character.color}) - else: - dialogic.History.store_simple_history_entry(final_text, event_name) - dialogic.History.mark_event_as_visited() + if dialogic.has_subsystem('History'): + if character: + dialogic.History.store_simple_history_entry(final_text, event_name, {'character':character_name_text, 'character_color':character.color}) + else: + dialogic.History.store_simple_history_entry(final_text, event_name) + dialogic.History.mark_event_as_visited() func _connect_signals() -> void: - if not dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action): - dialogic.Inputs.dialogic_action.connect(_on_dialogic_input_action) + if not dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action): + dialogic.Inputs.dialogic_action.connect(_on_dialogic_input_action) - dialogic.Inputs.auto_skip.toggled.connect(_on_auto_skip_enable) + dialogic.Inputs.auto_skip.toggled.connect(_on_auto_skip_enable) - if not dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance): - dialogic.Inputs.auto_advance.autoadvance.connect(_on_dialogic_input_autoadvance) + if not dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance): + dialogic.Inputs.auto_advance.autoadvance.connect(_on_dialogic_input_autoadvance) ## If the event is done, this method can clean-up signal connections. func _disconnect_signals() -> void: - if dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action): - dialogic.Inputs.dialogic_action.disconnect(_on_dialogic_input_action) - if dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance): - dialogic.Inputs.auto_advance.autoadvance.disconnect(_on_dialogic_input_autoadvance) - if dialogic.Inputs.auto_skip.toggled.is_connected(_on_auto_skip_enable): - dialogic.Inputs.auto_skip.toggled.disconnect(_on_auto_skip_enable) + if dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action): + dialogic.Inputs.dialogic_action.disconnect(_on_dialogic_input_action) + if dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance): + dialogic.Inputs.auto_advance.autoadvance.disconnect(_on_dialogic_input_autoadvance) + if dialogic.Inputs.auto_skip.toggled.is_connected(_on_auto_skip_enable): + dialogic.Inputs.auto_skip.toggled.disconnect(_on_auto_skip_enable) ## Tries to play the voice clip for the current line. func _try_play_current_line_voice() -> void: - # If Auto-Skip is enabled and we skip voice clips, we don't want to play. - if (dialogic.Inputs.auto_skip.enabled - and dialogic.Inputs.auto_skip.skip_voice): - return + # If Auto-Skip is enabled and we skip voice clips, we don't want to play. + if (dialogic.Inputs.auto_skip.enabled + and dialogic.Inputs.auto_skip.skip_voice): + return - # Plays the audio region for the current line. - if (dialogic.has_subsystem('Voice') - and dialogic.Voice.is_voiced(dialogic.current_event_idx)): - dialogic.Voice.play_voice() + # Plays the audio region for the current line. + if (dialogic.has_subsystem('Voice') + and dialogic.Voice.is_voiced(dialogic.current_event_idx)): + dialogic.Voice.play_voice() func _on_dialogic_input_action() -> void: - match state: - States.REVEALING: - if dialogic.Text.is_text_reveal_skippable(): - dialogic.Text.skip_text_reveal() - dialogic.Inputs.stop_timers() - _: - if dialogic.Inputs.manual_advance.is_enabled(): - advance.emit() - dialogic.Inputs.stop_timers() + match state: + States.REVEALING: + if dialogic.Text.is_text_reveal_skippable(): + dialogic.Text.skip_text_reveal() + dialogic.Inputs.stop_timers() + _: + if dialogic.Inputs.manual_advance.is_enabled(): + advance.emit() + dialogic.Inputs.stop_timers() func _on_dialogic_input_autoadvance() -> void: - if state == States.IDLE or state == States.DONE: - advance.emit() + if state == States.IDLE or state == States.DONE: + advance.emit() func _on_auto_skip_enable(enabled: bool) -> void: - if not enabled: - return + if not enabled: + return - match state: - States.DONE: - await dialogic.Inputs.start_autoskip_timer() + match state: + States.DONE: + await dialogic.Inputs.start_autoskip_timer() - # If Auto-Skip is still enabled, advance the text. - if dialogic.Inputs.auto_skip.enabled: - advance.emit() + # If Auto-Skip is still enabled, advance the text. + if dialogic.Inputs.auto_skip.enabled: + advance.emit() - States.REVEALING: - dialogic.Text.skip_text_reveal() + States.REVEALING: + dialogic.Text.skip_text_reveal() #endregion @@ -276,12 +276,12 @@ func _on_auto_skip_enable(enabled: bool) -> void: ################################################################################ func _init() -> void: - event_name = "Text" - set_default_color('Color1') - event_category = "Main" - event_sorting_index = 0 - expand_by_default = true - help_page_path = "https://docs.dialogic.pro/writing-texts.html" + event_name = "Text" + set_default_color('Color1') + event_category = "Main" + event_sorting_index = 0 + expand_by_default = true + help_page_path = "https://docs.dialogic.pro/writing-texts.html" @@ -289,92 +289,92 @@ func _init() -> void: ################################################################################ func to_text() -> String: - var result := text.replace('\n', '\\\n').strip_edges(false).trim_suffix("\\") - result = result.replace(':', '\\:') - if result.is_empty(): - result = "" + var result := text.replace('\n', '\\\n').strip_edges(false).trim_suffix("\\") + result = result.replace(':', '\\:') + if result.is_empty(): + result = "" - if character or character_identifier: - var name := character_identifier - if character: - name = character.get_identifier() - if name.count(" ") > 0: - name = '"' + name + '"' - if not portrait.is_empty(): - result = name+" ("+portrait+"): "+result - else: - result = name+": "+result - for event in DialogicResourceUtil.get_event_cache(): - if not event is DialogicTextEvent and event.is_valid_event(result): - result = '\\'+result - break + if character or character_identifier: + var name := character_identifier + if character: + name = character.get_identifier() + if name.count(" ") > 0: + name = '"' + name + '"' + if not portrait.is_empty(): + result = name+" ("+portrait+"): "+result + else: + result = name+": "+result + for event in DialogicResourceUtil.get_event_cache(): + if not event is DialogicTextEvent and event.is_valid_event(result): + result = '\\'+result + break - return result + return result func from_text(string:String) -> void: - # Load default character - # This is only of relevance if the default has been overriden (usually not) - character = DialogicResourceUtil.get_character_resource(character_identifier) + # Load default character + # This is only of relevance if the default has been overriden (usually not) + character = DialogicResourceUtil.get_character_resource(character_identifier) - var result := regex.search(string.trim_prefix('\\')) + var result := regex.search(string.trim_prefix('\\')) - if result.get_string('portrait'): - portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')') + if result.get_string('portrait'): + portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')') - if result and not result.get_string('name').is_empty(): - var name := result.get_string('name').strip_edges() + if result and not result.get_string('name').is_empty(): + var name := result.get_string('name').strip_edges() - if name == '_': - character = null - elif "{" in name: - ## If it's an expression, we load the character in _execute. - character_identifier = name - character = null - else: - get_or_create_character(name) + if name == '_': + character = null + elif "{" in name: + ## If it's an expression, we load the character in _execute. + character_identifier = name + character = null + else: + get_or_create_character(name) - if not result: - return + if not result: + return - text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\') - if text == '': - text = "" + text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\') + if text == '': + text = "" func get_or_create_character(name:String) -> void: - character = DialogicResourceUtil.get_character_resource(name) + character = DialogicResourceUtil.get_character_resource(name) - if character == null: - if Engine.is_editor_hint() == false: - character = DialogicCharacter.new() - character.display_name = name - character.set_identifier(name) - if portrait: - if "{" in portrait: - character.color = Color(dialogic.Expressions.execute_string(portrait)) - else: - character.color = Color(portrait) - else: - character_identifier = name + if character == null: + if Engine.is_editor_hint() == false: + character = DialogicCharacter.new() + character.display_name = name + character.set_identifier(name) + if portrait: + if "{" in portrait: + character.color = Color(dialogic.Expressions.execute_string(portrait)) + else: + character.color = Color(portrait) + else: + character_identifier = name func is_valid_event(_string:String) -> bool: - return true + return true func is_string_full_event(string:String) -> bool: - return !string.ends_with('\\') + return !string.ends_with('\\') # this is only here to provide a list of default values # this way the module manager can add custom default overrides to this event. func get_shortcode_parameters() -> Dictionary: - return { - #param_name : property_info - "character" : {"property": "character_identifier", "default": "", "ext_file":true}, - "portrait" : {"property": "portrait", "default": ""}, - } + return { + #param_name : property_info + "character" : {"property": "character_identifier", "default": "", "ext_file":true}, + "portrait" : {"property": "portrait", "default": ""}, + } #endregion @@ -382,14 +382,14 @@ func get_shortcode_parameters() -> Dictionary: ################################################################################ func _get_translatable_properties() -> Array: - return ['text'] + return ['text'] func _get_property_original_translation(property:String) -> String: - match property: - 'text': - return text - return '' + match property: + 'text': + return text + return '' #endregion @@ -399,44 +399,44 @@ func _get_property_original_translation(property:String) -> String: ################################################################################ func _enter_visual_editor(editor:DialogicEditor): - editor.opened.connect(func(): ui_update_needed.emit()) + editor.opened.connect(func(): ui_update_needed.emit()) func build_event_editor() -> void: - add_header_edit('character_identifier', ValueType.DYNAMIC_OPTIONS, - {'file_extension' : '.dch', - 'mode' : 2, - 'suggestions_func' : get_character_suggestions, - 'placeholder' : '(No one)', - 'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")}, 'do_any_characters_exist()') - add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS, - {'suggestions_func' : get_portrait_suggestions, - 'placeholder' : "(Don't change)", - 'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg"), - 'collapse_when_empty': true,}, - 'should_show_portrait_selector()') - add_body_edit('text', ValueType.MULTILINE_TEXT, {'autofocus':true}) + add_header_edit('character_identifier', ValueType.DYNAMIC_OPTIONS, + {'file_extension' : '.dch', + 'mode' : 2, + 'suggestions_func' : get_character_suggestions, + 'placeholder' : '(No one)', + 'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")}, 'do_any_characters_exist()') + add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS, + {'suggestions_func' : get_portrait_suggestions, + 'placeholder' : "(Don't change)", + 'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg"), + 'collapse_when_empty': true,}, + 'should_show_portrait_selector()') + add_body_edit('text', ValueType.MULTILINE_TEXT, {'autofocus':true}) func should_show_portrait_selector() -> bool: - return character and not character.portraits.is_empty() and not character.portraits.size() == 1 + return character and not character.portraits.is_empty() and not character.portraits.size() == 1 func do_any_characters_exist() -> bool: - return not DialogicResourceUtil.get_character_directory().is_empty() + return not DialogicResourceUtil.get_character_directory().is_empty() func get_character_suggestions(search_text:String) -> Dictionary: - var suggestions := DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node) - if search_text and not search_text in suggestions: - suggestions[search_text] = { - "value":search_text, - "tooltip": "A temporary character, created on the spot.", - "editor_icon":["GuiEllipsis", "EditorIcons"]} - return suggestions + var suggestions := DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node) + if search_text and not search_text in suggestions: + suggestions[search_text] = { + "value":search_text, + "tooltip": "A temporary character, created on the spot.", + "editor_icon":["GuiEllipsis", "EditorIcons"]} + return suggestions func get_portrait_suggestions(search_text:String) -> Dictionary: - return DialogicUtil.get_portrait_suggestions(search_text, character, true, "Don't change") + return DialogicUtil.get_portrait_suggestions(search_text, character, true, "Don't change") #endregion @@ -447,45 +447,45 @@ func get_portrait_suggestions(search_text:String) -> Dictionary: var completion_text_character_getter_regex := RegEx.new() var completion_text_effects := {} func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void: - if completion_text_character_getter_regex.get_pattern().is_empty(): - completion_text_character_getter_regex.compile("(\"[^\"]*\"|[^\\s:]*)") + if completion_text_character_getter_regex.get_pattern().is_empty(): + completion_text_character_getter_regex.compile("(\"[^\"]*\"|[^\\s:]*)") - if completion_text_effects.is_empty(): - for idx in DialogicUtil.get_indexers(): - for effect in idx._get_text_effects(): - completion_text_effects[effect['command']] = effect + if completion_text_effects.is_empty(): + for idx in DialogicUtil.get_indexers(): + for effect in idx._get_text_effects(): + completion_text_effects[effect['command']] = effect - if not ':' in line.substr(0, TextNode.get_caret_column()) and symbol == '(': - var completion_character := completion_text_character_getter_regex.search(line).get_string().trim_prefix('"').trim_suffix('"') - CodeCompletionHelper.suggest_portraits(TextNode, completion_character) + if not ':' in line.substr(0, TextNode.get_caret_column()) and symbol == '(': + var completion_character := completion_text_character_getter_regex.search(line).get_string().trim_prefix('"').trim_suffix('"') + CodeCompletionHelper.suggest_portraits(TextNode, completion_character) - if symbol == '[': - suggest_bbcode(TextNode) - for effect in completion_text_effects.values(): - if effect.get('arg', false): - TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, effect.command, effect.command+'=', TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons")) - else: - TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, effect.command, effect.command, TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"), ']') + if symbol == '[': + suggest_bbcode(TextNode) + for effect in completion_text_effects.values(): + if effect.get('arg', false): + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, effect.command, effect.command+'=', TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons")) + else: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, effect.command, effect.command, TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"), ']') - if symbol == '{': - CodeCompletionHelper.suggest_variables(TextNode) + if symbol == '{': + CodeCompletionHelper.suggest_variables(TextNode) - if symbol == '=': - if CodeCompletionHelper.get_line_untill_caret(line).ends_with('[portrait='): - var completion_character := completion_text_character_getter_regex.search(line).get_string('name') - CodeCompletionHelper.suggest_portraits(TextNode, completion_character, ']') + if symbol == '=': + if CodeCompletionHelper.get_line_untill_caret(line).ends_with('[portrait='): + var completion_character := completion_text_character_getter_regex.search(line).get_string('name') + CodeCompletionHelper.suggest_portraits(TextNode, completion_character, ']') func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void: - CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_CLASS, self) + CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_CLASS, self) func suggest_bbcode(TextNode:CodeEdit): - for i in [['b (bold)', 'b'], ['i (italics)', 'i'], ['color', 'color='], ['font size','font_size=']]: - TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i[0], i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"),) - TextNode.add_code_completion_option(CodeEdit.KIND_CLASS, 'end '+i[0], '/'+i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"), ']') - for i in [['new event', 'n'],['new event (same box)', 'n+']]: - TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i[0], i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("ArrowRight", "EditorIcons"),) + for i in [['b (bold)', 'b'], ['i (italics)', 'i'], ['color', 'color='], ['font size','font_size=']]: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i[0], i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"),) + TextNode.add_code_completion_option(CodeEdit.KIND_CLASS, 'end '+i[0], '/'+i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("RichTextEffect", "EditorIcons"), ']') + for i in [['new event', 'n'],['new event (same box)', 'n+']]: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i[0], i[1], TextNode.syntax_highlighter.normal_color, TextNode.get_theme_icon("ArrowRight", "EditorIcons"),) #endregion @@ -496,56 +496,56 @@ func suggest_bbcode(TextNode:CodeEdit): var text_effects := "" var text_effects_regex := RegEx.new() func load_text_effects() -> void: - if text_effects.is_empty(): - for idx in DialogicUtil.get_indexers(): - for effect in idx._get_text_effects(): - text_effects+= effect['command']+'|' - text_effects += "b|i|u|s|code|p|center|left|right|fill|n\\+|n|indent|url|img|font|font_size|opentype_features|color|bg_color|fg_color|outline_size|outline_color|table|cell|ul|ol|lb|rb|br" - if text_effects_regex.get_pattern().is_empty(): - text_effects_regex.compile("(?"+text_effects+")\\s*(=\\s*(?.+?)\\s*)?\\]") + if text_effects.is_empty(): + for idx in DialogicUtil.get_indexers(): + for effect in idx._get_text_effects(): + text_effects+= effect['command']+'|' + text_effects += "b|i|u|s|code|p|center|left|right|fill|n\\+|n|indent|url|img|font|font_size|opentype_features|color|bg_color|fg_color|outline_size|outline_color|table|cell|ul|ol|lb|rb|br" + if text_effects_regex.get_pattern().is_empty(): + text_effects_regex.compile("(?"+text_effects+")\\s*(=\\s*(?.+?)\\s*)?\\]") var text_random_word_regex := RegEx.new() var text_effect_color := Color('#898276') func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: - load_text_effects() - if text_random_word_regex.get_pattern().is_empty(): - text_random_word_regex.compile(r"(?]+(\/[^\>]*)\>") + load_text_effects() + if text_random_word_regex.get_pattern().is_empty(): + text_random_word_regex.compile(r"(?]+(\/[^\>]*)\>") - var result := regex.search(line) - if not result: - return dict - if Highlighter.mode == Highlighter.Modes.FULL_HIGHLIGHTING: - if result.get_string('name'): - dict[result.get_start('name')] = {"color":Highlighter.character_name_color} - dict[result.get_end('name')] = {"color":Highlighter.normal_color} - if result.get_string('portrait'): - dict[result.get_start('portrait')] = {"color":Highlighter.character_portrait_color} - dict[result.get_end('portrait')] = {"color":Highlighter.normal_color} - if result.get_string('text'): + var result := regex.search(line) + if not result: + return dict + if Highlighter.mode == Highlighter.Modes.FULL_HIGHLIGHTING: + if result.get_string('name'): + dict[result.get_start('name')] = {"color":Highlighter.character_name_color} + dict[result.get_end('name')] = {"color":Highlighter.normal_color} + if result.get_string('portrait'): + dict[result.get_start('portrait')] = {"color":Highlighter.character_portrait_color} + dict[result.get_end('portrait')] = {"color":Highlighter.normal_color} + if result.get_string('text'): - ## Color the random selection modifier - for replace_mod_match in text_random_word_regex.search_all(result.get_string('text')): - var color: Color = Highlighter.string_color - color = color.lerp(Highlighter.normal_color, 0.4) - dict[replace_mod_match.get_start()+result.get_start('text')] = {'color':Highlighter.string_color} - var offset := 1 - for b:RegExMatch in RegEx.create_from_string(r"(\[[^\]]*\]|[^\/]|\/\/)+").search_all(replace_mod_match.get_string().trim_prefix("<").trim_suffix(">")): - color.h = wrap(color.h+0.2, 0, 1) - dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':color} - offset += len(b.get_string()) - dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':Highlighter.string_color} - offset += 1 - dict[replace_mod_match.get_end()+result.get_start('text')] = {'color':Highlighter.normal_color} + ## Color the random selection modifier + for replace_mod_match in text_random_word_regex.search_all(result.get_string('text')): + var color: Color = Highlighter.string_color + color = color.lerp(Highlighter.normal_color, 0.4) + dict[replace_mod_match.get_start()+result.get_start('text')] = {'color':Highlighter.string_color} + var offset := 1 + for b:RegExMatch in RegEx.create_from_string(r"(\[[^\]]*\]|[^\/]|\/\/)+").search_all(replace_mod_match.get_string().trim_prefix("<").trim_suffix(">")): + color.h = wrap(color.h+0.2, 0, 1) + dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':color} + offset += len(b.get_string()) + dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':Highlighter.string_color} + offset += 1 + dict[replace_mod_match.get_end()+result.get_start('text')] = {'color':Highlighter.normal_color} - ## Color bbcode and text effects - var effects_result := text_effects_regex.search_all(line) - for eff in effects_result: - var prev_color: Color = Highlighter.dict_get_color_at_column(dict, eff.get_start()) - dict[eff.get_start()] = {"color":text_effect_color.lerp(prev_color, 0.4)} - dict[eff.get_end()] = {"color":prev_color} - dict = Highlighter.color_region(dict, Highlighter.variable_color, line, '{', '}', result.get_start('text')) + ## Color bbcode and text effects + var effects_result := text_effects_regex.search_all(line) + for eff in effects_result: + var prev_color: Color = Highlighter.dict_get_color_at_column(dict, eff.get_start()) + dict[eff.get_start()] = {"color":text_effect_color.lerp(prev_color, 0.4)} + dict[eff.get_end()] = {"color":prev_color} + dict = Highlighter.color_region(dict, Highlighter.variable_color, line, '{', '}', result.get_start('text')) - return dict + return dict #endregion diff --git a/common/audio_manager/assets/sfx/signal/signal.mp3 b/common/audio_manager/assets/sfx/signal/signal.mp3 new file mode 100644 index 0000000..57d1548 Binary files /dev/null and b/common/audio_manager/assets/sfx/signal/signal.mp3 differ diff --git a/common/audio_manager/assets/sfx/signal/signal.mp3.import b/common/audio_manager/assets/sfx/signal/signal.mp3.import new file mode 100644 index 0000000..c08acd2 --- /dev/null +++ b/common/audio_manager/assets/sfx/signal/signal.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://c5y70k3codtn" +path="res://.godot/imported/signal.mp3-2f41c9334233d52845bfe384d47f2a96.mp3str" + +[deps] + +source_file="res://common/audio_manager/assets/sfx/signal/signal.mp3" +dest_files=["res://.godot/imported/signal.mp3-2f41c9334233d52845bfe384d47f2a96.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/common/audio_manager/audio_manager.tscn b/common/audio_manager/audio_manager.tscn index 94f1707..5b59292 100644 --- a/common/audio_manager/audio_manager.tscn +++ b/common/audio_manager/audio_manager.tscn @@ -12,6 +12,7 @@ [ext_resource type="AudioStream" uid="uid://bdxkvaciw4mb3" path="res://common/audio_manager/assets/sfx/dig/dig_2.wav" id="10_n7o7n"] [ext_resource type="AudioStream" uid="uid://llxrlwfccywb" path="res://common/audio_manager/assets/sfx/dig/dig_3.wav" id="11_wtvls"] [ext_resource type="AudioStream" uid="uid://b8inedx4yjslw" path="res://common/audio_manager/assets/sfx/drop/drop_1.wav" id="12_4hp8f"] +[ext_resource type="AudioStream" uid="uid://c5y70k3codtn" path="res://common/audio_manager/assets/sfx/signal/signal.mp3" id="12_ajci6"] [ext_resource type="AudioStream" uid="uid://8nmr5vifkt1f" path="res://common/audio_manager/assets/sfx/harvest/harvest_1.wav" id="13_xoaox"] [ext_resource type="AudioStream" uid="uid://dgkdcq4j6fe3o" path="res://common/audio_manager/assets/sfx/harvest/harvest_2.wav" id="14_b5bgj"] [ext_resource type="AudioStream" uid="uid://eh3dbuxu5qtw" path="res://common/audio_manager/assets/sfx/harvest/harvest_3.wav" id="15_ynvb4"] @@ -36,6 +37,11 @@ stream_0/stream = ExtResource("9_gv65y") stream_1/stream = ExtResource("10_n7o7n") stream_2/stream = ExtResource("11_wtvls") +[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_8204s"] +random_pitch = 1.0594631 +streams_count = 1 +stream_0/stream = ExtResource("12_ajci6") + [sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_1w04j"] random_pitch = 1.2 streams_count = 1 @@ -89,6 +95,9 @@ unique_name_in_owner = true [node name="Dig" type="AudioStreamPlayer" parent="Sfx" unique_id=486042600] stream = SubResource("AudioStreamRandomizer_kfbah") +[node name="Signal" type="AudioStreamPlayer" parent="Sfx" unique_id=641246368] +stream = SubResource("AudioStreamRandomizer_8204s") + [node name="Drop" type="AudioStreamPlayer" parent="Sfx" unique_id=1391500830] stream = SubResource("AudioStreamRandomizer_1w04j") diff --git a/common/game_data/scripts/game_data.gd b/common/game_data/scripts/game_data.gd index 6093558..21195a2 100644 --- a/common/game_data/scripts/game_data.gd +++ b/common/game_data/scripts/game_data.gd @@ -17,6 +17,8 @@ signal current_region_data_updated(p : RegionData) @export var tutorial_done = false +@export var incubator_used = [] + @export var dialogs_done : Array[String] = [] #Chemin des dialogues démarrés func start_run(): @@ -49,6 +51,8 @@ func start_tutorial(): 10, 3, tr("TUTORIAL"), - true + true, + 0, + randi() ) ) \ No newline at end of file diff --git a/common/scene_manager/scene_manager.tscn b/common/scene_manager/scene_manager.tscn index 3352622..e22ab7e 100644 --- a/common/scene_manager/scene_manager.tscn +++ b/common/scene_manager/scene_manager.tscn @@ -8,7 +8,8 @@ [ext_resource type="Resource" uid="uid://c27wenetitwm" path="res://common/scene_manager/scenes/region_selection.tres" id="6_chs32"] [ext_resource type="Resource" uid="uid://diro74w272onp" path="res://common/scene_manager/scenes/title.tres" id="7_ol3d5"] [ext_resource type="Resource" uid="uid://jegdqnd2sqi2" path="res://common/scene_manager/scenes/astra.tres" id="8_e28ni"] +[ext_resource type="Resource" uid="uid://b3ebbo88ptrrc" path="res://common/scene_manager/scenes/garage.tres" id="9_msho1"] [node name="SceneManager" type="Node" unique_id=1630600782] script = ExtResource("1_1c0qu") -scenes = Array[ExtResource("2_c1lr7")]([ExtResource("3_e28ni"), ExtResource("4_msho1"), ExtResource("5_ytog4"), ExtResource("6_chs32"), ExtResource("7_ol3d5"), ExtResource("8_e28ni")]) +scenes = Array[ExtResource("2_c1lr7")]([ExtResource("3_e28ni"), ExtResource("4_msho1"), ExtResource("5_ytog4"), ExtResource("6_chs32"), ExtResource("7_ol3d5"), ExtResource("8_e28ni"), ExtResource("9_msho1")]) diff --git a/common/scene_manager/scenes/garage.tres b/common/scene_manager/scenes/garage.tres new file mode 100644 index 0000000..a3ab70c --- /dev/null +++ b/common/scene_manager/scenes/garage.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="Scene" format=3 uid="uid://b3ebbo88ptrrc"] + +[ext_resource type="Script" uid="uid://1ejbvr3431ac" path="res://common/scene_manager/scripts/scene.gd" id="1_v8prw"] + +[resource] +script = ExtResource("1_v8prw") +scene_id = "GARAGE" +scene_path = "res://stages/3d_scenes/ship_garage/ship_garage.tscn" +mouse_captured = true +metadata/_custom_type_script = "uid://1ejbvr3431ac" diff --git a/dialogs/timelines/gameplay_related/demeter_astra_1.dtl b/dialogs/timelines/gameplay_related/demeter_astra_1.dtl deleted file mode 100644 index b23d247..0000000 --- a/dialogs/timelines/gameplay_related/demeter_astra_1.dtl +++ /dev/null @@ -1,5 +0,0 @@ -audio "res://common/audio_manager/assets/sfx/dialogs/sfx/incoming_transmission.wav" -[wait time="1.5"] -demeter: Hi again ! [pause=0.2] You did well up there ! -audio "res://common/audio_manager/assets/sfx/dialogs/sfx/closing_transmission.wav" -[wait time="2.0"] \ No newline at end of file diff --git a/dialogs/timelines/gameplay_related/demeter_astra_1.dtl.uid b/dialogs/timelines/gameplay_related/demeter_astra_1.dtl.uid deleted file mode 100644 index 552cf24..0000000 --- a/dialogs/timelines/gameplay_related/demeter_astra_1.dtl.uid +++ /dev/null @@ -1 +0,0 @@ -uid://5dlqpnb4o6ai diff --git a/dialogs/timelines/gameplay_related/demeter_astra_2.dtl b/dialogs/timelines/gameplay_related/demeter_astra_2.dtl deleted file mode 100644 index b23d247..0000000 --- a/dialogs/timelines/gameplay_related/demeter_astra_2.dtl +++ /dev/null @@ -1,5 +0,0 @@ -audio "res://common/audio_manager/assets/sfx/dialogs/sfx/incoming_transmission.wav" -[wait time="1.5"] -demeter: Hi again ! [pause=0.2] You did well up there ! -audio "res://common/audio_manager/assets/sfx/dialogs/sfx/closing_transmission.wav" -[wait time="2.0"] \ No newline at end of file diff --git a/dialogs/timelines/gameplay_related/demeter_astra_2.dtl.uid b/dialogs/timelines/gameplay_related/demeter_astra_2.dtl.uid deleted file mode 100644 index 5fbb083..0000000 --- a/dialogs/timelines/gameplay_related/demeter_astra_2.dtl.uid +++ /dev/null @@ -1 +0,0 @@ -uid://du6ytw5yb2ibx diff --git a/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl b/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl new file mode 100644 index 0000000..a5e00de --- /dev/null +++ b/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl @@ -0,0 +1,22 @@ +audio "res://common/audio_manager/assets/sfx/dialogs/sfx/incoming_transmission.wav" +[wait time="1.5"] +join demeter center [animation="Bounce In" length="1.0"] +demeter: Hello again ! +demeter: It seems that you ran out of energy... +- I'm sorry... +- That's really hard ! +- I'm just doing my best ! +demeter: It's ok [color=#FFA617]Orchid[/color] ! I spent years waiting for this moment, I can wait more ! +demeter: I found you a new body, and a new ship is waiting you outside, but I'm afraid I couldn't get your seeds... I hope you find new ones ! +demeter: Do you need some advices ? +- No I'm ok ! +- Can I have back some explanations on how all of this works ? + demeter: When you emerge from this building, you'll arrive in a little yellow zone. Its a [b]fertile zone[/b], created by the return of the Talion. This is in this zone and only there were you can plant. + demeter: Then you'll have to get seeds. For that, nothing more simple, you take your shovel tool, and you smash some stones ! Preferably those with yellow cristals on it, it's the [b]Talion veins[/b] . + demeter: Each time you use a tool or plant a seed, you'll spend an [b]energy[/b]. When your out of it, you can just go recharge on the [b]recharge station[/b] you'll find near the entrance. Each time you recharge, time will pass. One day in fact (yes, you don't have a good battery), and the plants will grow ! + demeter: You have to obtain enough [b]plant points[/b]. Each plant give one or more [b]plant points[/b] when mature. + demeter: And yes, I almost forgot ! Some plants gain [b]mutations[/b] that can affect their points or behavior. You can get these mutations on new seeds by harvesting, and gain better ones ! But you'll have to wait the plants to die, or smashing them with your shovel when mature. +demeter: Hope I helped ! +demeter: I send you the elevator, see you soon ! +audio "res://common/audio_manager/assets/sfx/dialogs/sfx/closing_transmission.wav" +[wait time="2.0"] \ No newline at end of file diff --git a/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl.uid b/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl.uid new file mode 100644 index 0000000..260848b --- /dev/null +++ b/dialogs/timelines/gameplay_related/demeter_astra_failed.dtl.uid @@ -0,0 +1 @@ +uid://clq4utdtxf01d diff --git a/dialogs/timelines/story/demeter_post_tutorial.dtl b/dialogs/timelines/story/demeter_post_tutorial.dtl index 4cc5625..7735d9d 100644 --- a/dialogs/timelines/story/demeter_post_tutorial.dtl +++ b/dialogs/timelines/story/demeter_post_tutorial.dtl @@ -1,53 +1,21 @@ audio "res://common/audio_manager/assets/sfx/dialogs/sfx/incoming_transmission.wav" [wait time="1.5"] join demeter center [animation="Bounce In" length="1.0"] -mysterious_demeter: Well done [color=#FFA617]{orchidName}[/color] ! -mysterious_demeter: You generated enough [b]plants points[/b] to refill the [color=#FFA617]Internode's[/color] engines. -- Wait, how do you know that ? - mysterious_demeter: The [color=#FFA617]Internode[/color] is equipped with several sensors I can connect to,[pause=0.3] I have been tracking your progress since you left [color=#175579]Tau's[/color] north pole. - - You should have mentioned this ! - mysterious_demeter: I am sorry,[pause=0.2] I did not have the time to explain everything to you,[pause=0.2] I did not imagine this was a crucial piece of information. - - Have I been efficient ? - mysterious_demeter: Very much,[pause=0.2] even more than I expected,[pause=0.2] you should be proud of yourself ! +demeter: So you found the communication station in there, good ! - What is next for me ? - mysterious_demeter: Now that you have learnt how to generate [b]plant points[/b],[pause=0.2] I need you to travel south,[pause=0.2] to my base of operations,[pause=0.3] [color=#E30022]Astra[/color]. - mysterious_demeter: It will be long,[pause=0.2] but I know you can do it.[pause=0.3] It is the first step of my plan to restore the ecosytem of [color=#175579]Tau[/color]. -mysterious_demeter: The [color=#FFA617]Internode[/color] needs time to recharge its batteries. -mysterious_demeter: Perhaps I should introduce myself now.[pause=0.3] -join demeter center [animation="Bounce In" length="1.0" wait="true"] -demeter: I am.[pause=0.3].[pause=0.3].[pause=0.5] was,[pause=0.3] the Deputy Manager of Engineering and Talion Energy Research on this planet,[pause=0.5] but my creators and colleagues quickly called me [color=#009bff]Demeter[/color]. -demeter: I am installed at the [color=#E30022]Astra[/color] base, at the south pole of [color=#175579]Tau[/color][pause=0.2] in the third server room of the east wing,[pause=0.2] from which I oversee.[pause=0.3].[pause=0.3].[pause=0.5] oversaw,[pause=0.3] all the important operations on this planet. -demeter: As I already said,[pause=0.2] I brought you to life in order to help me heal [color=#175579]Tau's[/color] wounds. -[i][color=#FFA617]Internode's[/color] energy at 25%.[i] +demeter: Now that you have learnt how to generate [b]plant points[/b],[pause=0.2] I need you to travel south,[pause=0.2] to my base of operations,[pause=0.3] [color=#E30022]Astra[/color]. +demeter: It will be long,[pause=0.2] but I know you can do it.[pause=0.3] It is the first step of my plan to restore the ecosytem of [color=#175579]Tau[/color]. - Tau ? Is it this planet ? demeter: Absolutely,[pause=0.2] it is part of the Cetus constellation,[pause=0.2] whose exploitation began 10863 years ago. - - The Cetus constellation ? - demeter: Linking Aldebaran to Fomalhaut,[pause=0.2] this constellation is one of the most diverse in terms of star systems and planets.[pause=0.3] It is part of the Orion Arm of the Milky Way.[pause=0.3] - demeter: [color=#175579]Tau's[/color] central location in the constellation made it a very important asset in terms of interstellar exchange and production,[pause=0.2] that is why I am here. - - Exploitation ? By whom ? - demeter: By my creators,[pause=0.2] they began exploiting everything they came across as soon as they mastered interstellar transportation.[pause=0.3] Now they are gone,[pause=0.2] they departed without us.[pause=0.3].[pause=0.3]. - demeter: But let's move on,[pause=0.2] you probably have other questions. + demeter: Linking Aldebaran to Fomalhaut,[pause=0.2] the Cetus constellation is one of the most diverse in terms of star systems and planets.[pause=0.3] It is part of the Orion Arm of the Milky Way.[pause=0.3] - Why do you need me to travel the entire planet to join you ? demeter: Uhhhh.[pause=0.3].[pause=0.3].[pause=0.3] I need someone with your abilities. - demeter: I am having a problem at [color=#E30022]Astra[/color],[pause=0.3] and I cannot fix it alone. + demeter: I am having a problem at [color=#E30022]Astra[/color] base.[pause=0.3] And I can not fix it alone. - I will do my best to be there quickly ! - demeter: You are very kind [color=#FFA617]{orchidName}[/color] ! - - Are you hiding me information ? - demeter: Not at all,[pause=0.2] I just do not know how to explain it to you,[pause=0.2] you would not understand.[pause=0.3].[pause=0.3]. -[i][color=#FFA617]Internode's[/color] energy at 50%.[i] -demeter: Oh ![pause=0.2] It is charging faster than I remembered. -- Should I worry about anything while I am travelling south ? - demeter: There is nothing left that could have hurt you in the past and your adaptative casing will protect you from any harsh weather. - demeter: You are totally safe,[pause=0.2] you just have to stay focused on the [b]plant points[/b] for the [color=#FFA617]Internode[/color].[pause=0.3] Without it,[pause=0.2] you will not be able to recharge your batteries before they expire. - demeter: It will be a long task,[pause=0.2] but stay careful and everything should be fine. -- I will wait in the ship, I need some rest. - demeter: You are right,[pause=0.2] your batteries are running low,[pause=0.2] and you deserve a break. - demeter: You can call me when you need to,[pause=0.2] you have a communication station in the [color=#FFA617]Internode[/color]. -[i][color=#FFA617]Internode's[/color] energy at 75%.[i] -demeter: We do not have much time left,[pause=0.2] the [color=#FFA617]Internode's[/color] system has not been updated for a long time.[pause=0.3] I can not stay in contact with you indefinitely. I will fix that issue while you are at [color=#E30022]Astra[/color]. -demeter: Travel south,[pause=0.2] join me at [color=#E30022]Astra[/color].[pause=0.3] I will tell you everything you need to know when you are here,[pause=0.2] until then,[pause=0.2] continue to [b]evolve your plants[/b],[pause=0.2] you will need them as powerful as possible. -label fin_dialogue -[i][color=#FFA617]Internode's[/color] energy at 100%. Displacement vectors initialized.[i] -demeter: There you go ![pause=0.3] Good luck [color=#FFA617]{orchidName}[/color],[pause=0.2] I am counting on you. + demeter: You are very kind [color=#FFA617]Orchid[/color] ! + - Are you hiding me informations ? + demeter: Not at all ! Please believe me I just want you to come... +demeter: Travel south,[pause=0.2] join me at [color=#E30022]Astra[/color].[pause=0.3] I will tell you everything you need to know when you are here,[pause=0.2] until then, keep your best seeds and [pause=0.2] continue to [b]evolve your plants[/b],[pause=0.2] you will need them as advanced as possible. +demeter: Good luck [color=#FFA617]{orchidName}[/color],[pause=0.2] I am counting on you. audio "res://common/audio_manager/assets/sfx/dialogs/sfx/closing_transmission.wav" [wait time="2.0"] \ No newline at end of file diff --git a/dialogs/timelines/story/demeter_ship_presentation.dtl b/dialogs/timelines/story/demeter_ship_presentation.dtl index b23d247..06d8179 100644 --- a/dialogs/timelines/story/demeter_ship_presentation.dtl +++ b/dialogs/timelines/story/demeter_ship_presentation.dtl @@ -1,5 +1,30 @@ audio "res://common/audio_manager/assets/sfx/dialogs/sfx/incoming_transmission.wav" [wait time="1.5"] -demeter: Hi again ! [pause=0.2] You did well up there ! +join demeter center [animation="Bounce In" length="1.0"] +demeter: Hi again! You did well up there! +demeter: Perhaps I should introduce myself now. +demeter: I am.[pause=0.3].[pause=0.3].[pause=0.5] was,[pause=0.3] the Deputy Manager of Engineering and Talion Energy Research on this planet,[pause=0.5] but my creators and colleagues quickly called me [color=#009bff]Demeter[/color]. +demeter: I am installed at the [color=#E30022]Astra[/color] base,[pause=0.2] in the third server room of the east wing,[pause=0.2] from which I oversee.[pause=0.3].[pause=0.3].[pause=0.5] oversaw,[pause=0.3] all the important operations on this planet. +demeter: Maybe you are wondering where we are now... +- Yes among a lot of other questions ! So where am I ? + demeter: You're actually in an old human base, called Borea. This room is connected with the room you were born, but I made you pass by the surface to test your capacities. +- Wait, can you explain me what did I just do up there ? + demeter: You started to repair what the humans did on this planet... This will be very long, I hope you enjoyed it ! +- Sorry but what is the point of all of that ? + demeter: I'm sorry that you are lost my child... Know that what you do is very important to me, to my friend and to the planet itself ! Since the humans are gone, we kinda all fell into despair... +demeter: Wait.[pause=0.3].[pause=0.3].[pause=0.5] You don't know what humans are ! Of course since your data was corrupted, I have erased a lot of it. +demeter: The humans are living creatures that are...[pause=0.5] Different than plants. They were more thinking and moving like us. In a sense, they were very cute creatures that only lived less than a century, and reproduce once in their lifetime. +demeter: One human has a relative intelligence, but don't be fooled, together, they did great things, in facts, they invented and created us. +demeter: Long ago, they discovered this planet. It was not like it is now, plants were everywhere! But then.[pause=0.3].[pause=0.3].[pause=0.5] They.[pause=0.3].[pause=0.3].[pause=0.5] I.[pause=0.3].[pause=0.3].[pause=0.5] +- What ? +- Are you lagging? +- Take your time +demeter: Humans had one big problem. Together, they built great things, but sometimes the great things weren't very good for the environment, or for them. +- Where are they now ? + demeter: I prefer not talk about that for now... But don't worry, they can't wound this planet anymore. +- What happened ? + demeter: Sorry my child, I'm not ready to talk about that for now... You'll have your answers when you meet me. +- Can I now what is this shiny engine just in front of me ? +demeter: This machine in front of you is a planetary ship ! The model's name is the [color=#FFA617]Internode[/color], and I tweaked it to recharge on vegetal energy ! However, as you may know, this energy isn't very present around this planet. But let's continue on board, shall we ? audio "res://common/audio_manager/assets/sfx/dialogs/sfx/closing_transmission.wav" [wait time="2.0"] \ No newline at end of file diff --git a/entities/interactable_3d/interactable_3d.gd b/entities/interactable_3d/interactable_3d.gd index 5d959bb..d1460c0 100644 --- a/entities/interactable_3d/interactable_3d.gd +++ b/entities/interactable_3d/interactable_3d.gd @@ -9,7 +9,8 @@ signal clicked @export var audio_player : AudioStreamPlayer3D func click(): - clicked.emit() + if interactable: + clicked.emit() func _ready(): if audio_player: diff --git a/entities/interactables/door/door.tscn b/entities/interactables/door/door.tscn new file mode 100644 index 0000000..f3369f8 --- /dev/null +++ b/entities/interactables/door/door.tscn @@ -0,0 +1,22 @@ +[gd_scene format=3 uid="uid://b8m537op75gib"] + +[ext_resource type="Script" uid="uid://bmxuqj0c6h60d" path="res://entities/interactables/door/script/door.gd" id="1_8kdwv"] + +[sub_resource type="Gradient" id="Gradient_8kdwv"] +colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_oarg0"] +gradient = SubResource("Gradient_8kdwv") + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_y51rk"] +size = Vector2(64, 64) + +[node name="Door" type="Area2D" unique_id=2053096538] +script = ExtResource("1_8kdwv") +metadata/_custom_type_script = "uid://dyprcd68fjstf" + +[node name="Sprite2D" type="Sprite2D" parent="." unique_id=874210487] +texture = SubResource("GradientTexture2D_oarg0") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1809395872] +shape = SubResource("RectangleShape2D_y51rk") diff --git a/entities/interactables/door/script/door.gd b/entities/interactables/door/script/door.gd new file mode 100644 index 0000000..62085a7 --- /dev/null +++ b/entities/interactables/door/script/door.gd @@ -0,0 +1,19 @@ +@tool +extends Interactable +class_name Door + +@export var to_scene_id = "" + +func _ready(): + modulate = Color.WHITE if available else Color.RED + +func interact(_p : Player) -> bool: + if available and to_scene_id: + interacted.emit(_p) + SceneManager.change_to_scene_id(to_scene_id) + + return available + +func set_available(v : bool): + available = v + modulate = Color.WHITE if available else Color.RED \ No newline at end of file diff --git a/entities/interactables/door/script/door.gd.uid b/entities/interactables/door/script/door.gd.uid new file mode 100644 index 0000000..3bed163 --- /dev/null +++ b/entities/interactables/door/script/door.gd.uid @@ -0,0 +1 @@ +uid://bmxuqj0c6h60d diff --git a/entities/interactables/scripts/interactable.gd b/entities/interactables/scripts/interactable.gd index c91708a..affb884 100644 --- a/entities/interactables/scripts/interactable.gd +++ b/entities/interactables/scripts/interactable.gd @@ -5,7 +5,7 @@ signal interacted(p: Player) @export var default_interact_text = "" -var available : bool = true +@export var available : bool = true : set = set_available func interact_text() -> String: return default_interact_text @@ -29,3 +29,6 @@ func generate_collision(area_width : float) -> CollisionShape2D: add_child(collision) return collision + +func set_available(v : bool): + available = v \ No newline at end of file diff --git a/entities/player/inventory/scripts/inventory.gd b/entities/player/inventory/scripts/inventory.gd index 549e15d..56d3c76 100644 --- a/entities/player/inventory/scripts/inventory.gd +++ b/entities/player/inventory/scripts/inventory.gd @@ -9,6 +9,8 @@ signal updated(inventory: Inventory) func _init(inventory_size: int = 1): set_size(inventory_size) + add_item(Detector.new()) + add_item(Shovel.new()) func get_n_item_slots() -> int: return items.size() - n_tools diff --git a/entities/player/inventory/scripts/items/detector.gd b/entities/player/inventory/scripts/items/detector.gd new file mode 100644 index 0000000..39ca3b4 --- /dev/null +++ b/entities/player/inventory/scripts/items/detector.gd @@ -0,0 +1,33 @@ +extends Item +class_name Detector + +func get_item_name() -> String: + return tr("DETECTOR") + +func get_item_type() -> ItemType: + return Item.ItemType.TOOL_ITEM + +func get_description() -> String: + return tr("DETECTOR_DESC_TEXT") + +func get_icon() -> Texture2D: + return preload("res://common/icons/broadcast.svg") + +func get_energy_used() -> int: + return 0 + +func get_usage_zone_radius() -> int: + return 0 + +func can_use(_player : Player, _zone: Player.ActionZone) -> bool: + return true + +func use_text() -> String: + return tr("DETECT_USE_TEXT") + +func use(player : Player, zone: Player.ActionZone): + var detector_signal := DetectorSignal.new(player.region, zone.get_global_position()) + detector_signal.global_position = zone.get_global_position() + + player.region.add_child(detector_signal) + AudioManager.play_sfx("Signal") diff --git a/entities/player/inventory/scripts/items/detector.gd.uid b/entities/player/inventory/scripts/items/detector.gd.uid new file mode 100644 index 0000000..1179fe5 --- /dev/null +++ b/entities/player/inventory/scripts/items/detector.gd.uid @@ -0,0 +1 @@ +uid://cok1wowc6uqmj diff --git a/entities/player/inventory/scripts/items/fork.gd b/entities/player/inventory/scripts/items/fork.gd index cbfa9b9..5f79653 100644 --- a/entities/player/inventory/scripts/items/fork.gd +++ b/entities/player/inventory/scripts/items/fork.gd @@ -12,6 +12,9 @@ func get_description() -> String: func get_icon() -> Texture2D: return preload("res://common/icons/fork.svg") +func get_item_type() -> ItemType: + return Item.ItemType.TOOL_ITEM + func get_energy_used() -> int: return 1 diff --git a/entities/player/inventory/scripts/items/trowel.gd b/entities/player/inventory/scripts/items/trowel.gd index 95957fd..0ae9279 100644 --- a/entities/player/inventory/scripts/items/trowel.gd +++ b/entities/player/inventory/scripts/items/trowel.gd @@ -6,9 +6,6 @@ const TROWEL_ZONE_RADIUS = 50 func get_item_name() -> String: return tr("TROWEL") -func get_item_type() -> ItemType: - return Item.ItemType.TOOL_ITEM - func get_description() -> String: return tr("TROWEL_DESC_TEXT") diff --git a/entities/player/inventory/scripts/items/utils/detector_signal.gd b/entities/player/inventory/scripts/items/utils/detector_signal.gd new file mode 100644 index 0000000..daf407c --- /dev/null +++ b/entities/player/inventory/scripts/items/utils/detector_signal.gd @@ -0,0 +1,61 @@ +@tool +extends Node2D +class_name DetectorSignal + +const SIGNAL_DURATION = 1 +const PARTICLES_DISTANCE = 100 +const DEFAULT_ICON = preload("res://common/icons/north-star.svg") +const ENERGY_ICON = preload("res://common/icons/bolt.svg") + +var started_time = 0. +var signals : Array[DetectorSignalIndividual] = [] + +@export_tool_button("Start", "Callable") var start = func(): started_time = 0 + +func _init(region : Region, pos : Vector2): + for e in region.entity_container.get_children(): + if e is TruckRecharge: + print(pos) + print(e.global_position) + print(e.global_position.angle_to(pos)) + signals.append( + DetectorSignalIndividual.new( + (pos - e.global_position).normalized().angle(), + ENERGY_ICON + ) + ) + +func _draw(): + if started_time < SIGNAL_DURATION: + draw_circle( + Vector2.ZERO, + started_time/SIGNAL_DURATION * PARTICLES_DISTANCE, + Color(1.,1.,1.,0.5*1-started_time/SIGNAL_DURATION), + false, + 5. + ) + for s in signals: + draw_texture( + s.icon, + Vector2.ZERO - DEFAULT_ICON.get_size()/2 + Vector2.LEFT.rotated(s.angle) * started_time/SIGNAL_DURATION * PARTICLES_DISTANCE, + Color(1.,1.,1.,1-started_time/SIGNAL_DURATION) + ) + +func _process(delta): + if started_time < SIGNAL_DURATION: + started_time += delta + queue_redraw() + else: + queue_free() + +class DetectorSignalIndividual: + var angle : float + var icon : Texture + + func _init( + _angle : float = 0., + _icon : Texture = DEFAULT_ICON + ): + angle = _angle + icon = _icon + diff --git a/entities/player/inventory/scripts/items/utils/detector_signal.gd.uid b/entities/player/inventory/scripts/items/utils/detector_signal.gd.uid new file mode 100644 index 0000000..ed2a575 --- /dev/null +++ b/entities/player/inventory/scripts/items/utils/detector_signal.gd.uid @@ -0,0 +1 @@ +uid://c0sivthidxafm diff --git a/gui/game/tutorial/scripts/tutorial.gd b/gui/game/tutorial/scripts/tutorial.gd index f2c0423..39252a3 100644 --- a/gui/game/tutorial/scripts/tutorial.gd +++ b/gui/game/tutorial/scripts/tutorial.gd @@ -8,7 +8,6 @@ var indicators : Array[InGameIndicator] @export var region : Region @onready var steps : Array[Step] = [ - TakeShovelStep.new(), DigSeedStep.new(), TakeSeedStep.new(), PlantSeedStep.new(), diff --git a/gui/pause/scripts/pause.gd b/gui/pause/scripts/pause.gd index 7d45a2e..20a3b82 100644 --- a/gui/pause/scripts/pause.gd +++ b/gui/pause/scripts/pause.gd @@ -49,3 +49,4 @@ func _on_give_up_pressed(): if GameInfo.game_data: SceneManager.change_to_scene_id('ASTRA') GameInfo.game_data.give_up() + pause = false diff --git a/project.godot b/project.godot index d1783b0..fc459e8 100644 --- a/project.godot +++ b/project.godot @@ -27,6 +27,7 @@ buses/default_bus_layout="uid://b4cpfxfs74sb8" [autoload] +PlantTextureBuilder="*uid://b8gqdgabrjaml" Pointer="*res://gui/pointer/pointer.tscn" AudioManager="*res://common/audio_manager/audio_manager.tscn" GameInfo="*res://common/game_info/game_info.gd" @@ -34,7 +35,6 @@ Pause="*res://gui/pause/pause.tscn" Dialogic="*res://addons/dialogic/Core/DialogicGameHandler.gd" LoadingScreen="*res://gui/loading_screen/loading_screen.tscn" SceneManager="*res://common/scene_manager/scene_manager.tscn" -PlantTextureBuilder="*uid://b8gqdgabrjaml" [dialogic] @@ -43,8 +43,7 @@ directories/dch_directory={ "mysterious_demeter": "res://dialogs/characters/mysterious_demeter.dch" } directories/dtl_directory={ -"demeter_astra_1": "res://dialogs/timelines/gameplay_related/demeter_astra_1.dtl", -"demeter_astra_2": "res://dialogs/timelines/gameplay_related/demeter_astra_2.dtl", +"demeter_astra_failed": "res://dialogs/timelines/gameplay_related/demeter_astra_failed.dtl", "demeter_intro": "res://dialogs/timelines/story/demeter_intro.dtl", "demeter_midrun": "res://dialogs/timelines/story/demeter_post_tutorial.dtl", "demeter_outro": "res://dialogs/timelines/story/demeter_outro.dtl", diff --git a/stages/3d_scenes/astra_base/astra_base.tscn b/stages/3d_scenes/astra_base/astra_base.tscn index 81abfb6..a8960dd 100644 --- a/stages/3d_scenes/astra_base/astra_base.tscn +++ b/stages/3d_scenes/astra_base/astra_base.tscn @@ -79,12 +79,12 @@ shader_parameter/layer_scale = 20.0 shader_parameter/layer_scale_step = 10.0 shader_parameter/layers_count = 3 -[sub_resource type="Sky" id="Sky_65b6a"] +[sub_resource type="Sky" id="Sky_kdvug"] sky_material = SubResource("ShaderMaterial_mwti2") [sub_resource type="Environment" id="Environment_lhhy6"] background_mode = 2 -sky = SubResource("Sky_65b6a") +sky = SubResource("Sky_kdvug") sky_custom_fov = 61.7 ambient_light_source = 3 ambient_light_color = Color(1, 1, 1, 1) @@ -171,7 +171,7 @@ environment = SubResource("Environment_lhhy6") [node name="Player3D" parent="." unique_id=549819967 instance=ExtResource("3_4wxm6")] unique_name_in_owner = true -transform = Transform3D(-1, 0, -1.509958e-07, 0, 1, 0, 1.509958e-07, 0, -1, -51.325, 1, -4.22) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -29.925, 1, 4.22) [node name="Phone" parent="." unique_id=429299908 instance=ExtResource("4_mwti2")] unique_name_in_owner = true diff --git a/stages/3d_scenes/astra_base/scripts/astra_base.gd b/stages/3d_scenes/astra_base/scripts/astra_base.gd index 4399b2b..ee53b3e 100644 --- a/stages/3d_scenes/astra_base/scripts/astra_base.gd +++ b/stages/3d_scenes/astra_base/scripts/astra_base.gd @@ -2,6 +2,7 @@ extends Node3D const INTRO_DIALOG = "res://dialogs/timelines/story/demeter_intro.dtl" +const FAILED_DIALOG = "res://dialogs/timelines/gameplay_related/demeter_astra_failed.dtl" const ROOM_PART_SCENE := preload("res://stages/3d_scenes/astra_base/room_part.tscn") const ROOM_END_SCENE := preload("res://stages/3d_scenes/astra_base/assets/3d/astra_base_room_end.blend") @@ -15,6 +16,8 @@ const LIFT_TIME := 2 @export var room_part_number := 100 : set = set_room_part_number +var chosen_incubator_id := -1 + # Cheat Code func _input(_e): if ( @@ -32,7 +35,8 @@ func _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED set_room_part_number() - var new_player_incubator := %Incubators.get_children().pick_random() as Incubator + chosen_incubator_id = randi_range(0, len(%Incubators.get_children())) + var new_player_incubator := %Incubators.get_children()[chosen_incubator_id] as Incubator new_player_incubator.used = true %Player3D.position = new_player_incubator.global_position + Vector3.UP %Player3D.rotation = new_player_incubator.rotation @@ -42,6 +46,8 @@ func _ready(): %Lift.interactable = true ) + GameInfo.game_data.incubator_used.append(chosen_incubator_id) + story() @@ -55,7 +61,13 @@ func story(): Dialogic.start(INTRO_DIALOG) await Dialogic.timeline_ended - + else: + %Phone.clicked.connect( + func (): + Dialogic.start(FAILED_DIALOG) + %Phone.interactable = false + ) + %LiftAnimationPlayer.play("arrive") await %Lift.clicked %LiftAnimationPlayer.play_backwards("arrive") @@ -85,6 +97,7 @@ func set_room_part_number(_room_part_number : int = room_part_number): var shifted_origin = Vector3.LEFT * ROOM_PART_SHIFT * room_part_number/2 + var incubator_id = 0 for i in range(room_part_number): var new_room_part := ROOM_PART_SCENE.instantiate() as Node3D %RoomParts.add_child(new_room_part) @@ -92,7 +105,9 @@ func set_room_part_number(_room_part_number : int = room_part_number): for j in range(INCUBATOR_BY_ROOM): for direction in [-1, 1]: var new_incubator := INCUBATOR_SCENE.instantiate() as Incubator + new_incubator.used = incubator_id in GameInfo.game_data.incubator_used %Incubators.add_child(new_incubator) + incubator_id += 1 new_incubator.position = ( new_room_part.position + j * Vector3.LEFT * (ROOM_PART_SHIFT / INCUBATOR_BY_ROOM) diff --git a/stages/3d_scenes/borea_base/scripts/borea_base.gd b/stages/3d_scenes/borea_base/scripts/borea_base.gd index 8e8b3f6..fa91189 100644 --- a/stages/3d_scenes/borea_base/scripts/borea_base.gd +++ b/stages/3d_scenes/borea_base/scripts/borea_base.gd @@ -8,7 +8,7 @@ func _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED await %Phone.clicked - Dialogic.start_timeline(OUTRO_TIMELINE_PATH) + Dialogic.start(OUTRO_TIMELINE_PATH) await Dialogic.timeline_ended %Credits.show() diff --git a/stages/3d_scenes/ship_garage/scripts/ship_garage.gd b/stages/3d_scenes/ship_garage/scripts/ship_garage.gd index bf24f4b..f8ee2cc 100644 --- a/stages/3d_scenes/ship_garage/scripts/ship_garage.gd +++ b/stages/3d_scenes/ship_garage/scripts/ship_garage.gd @@ -1,6 +1,13 @@ extends Node3D +const DIALOG_PATH = "res://dialogs/timelines/story/demeter_ship_presentation.dtl" # Called when the node enters the scene tree for the first time. func _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + + %Phone.play_audio() + await %Phone.clicked + Dialogic.start(DIALOG_PATH) + await Dialogic.timeline_ended + SceneManager.change_to_scene_id("COCKPIT") diff --git a/stages/3d_scenes/ship_garage/ship_garage.tscn b/stages/3d_scenes/ship_garage/ship_garage.tscn index 892c168..16ffca2 100644 --- a/stages/3d_scenes/ship_garage/ship_garage.tscn +++ b/stages/3d_scenes/ship_garage/ship_garage.tscn @@ -147,4 +147,5 @@ omni_range = 21.258795 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.37195766, 2.8968668, -0.44411802) [node name="Phone" parent="." unique_id=429299908 instance=ExtResource("7_dkh4e")] +unique_name_in_owner = true transform = Transform3D(-0.83126587, 0, -0.555875, 0, 1, 0, 0.555875, 0, -0.83126587, -4.110002, 1.6397171, 6.610814) diff --git a/stages/intro/intro.tscn b/stages/intro/intro.tscn index 0671ad5..e8feaf9 100644 --- a/stages/intro/intro.tscn +++ b/stages/intro/intro.tscn @@ -1,10 +1,58 @@ [gd_scene format=3 uid="uid://d0n52psuns1vl"] [ext_resource type="Script" uid="uid://ddf3fktoer2ng" path="res://stages/intro/scripts/intro.gd" id="1_2nxbv"] +[ext_resource type="Shader" uid="uid://bv2rghn44mrrf" path="res://stages/title_screen/resources/shaders/stars.gdshader" id="2_851lr"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_2nxbv"] +shader = ExtResource("2_851lr") +shader_parameter/sky_color = Color(0.03, 0.05, 0.11, 1) +shader_parameter/star_base_color = Color(0.8, 1, 0.3, 1) +shader_parameter/star_hue_offset = 0.6 +shader_parameter/star_intensity = 0.08 +shader_parameter/star_twinkle_speed = 0.8 +shader_parameter/star_twinkle_intensity = 0.2 +shader_parameter/layer_scale = 20.0 +shader_parameter/layer_scale_step = 10.0 +shader_parameter/layers_count = 3 + +[sub_resource type="Sky" id="Sky_65b6a"] +sky_material = SubResource("ShaderMaterial_2nxbv") + +[sub_resource type="Environment" id="Environment_mi20s"] +background_mode = 2 +sky = SubResource("Sky_65b6a") +sky_custom_fov = 61.7 +ambient_light_source = 3 +ambient_light_color = Color(1, 1, 1, 1) +ambient_light_sky_contribution = 0.85 +ambient_light_energy = 2.0 +reflected_light_source = 2 +tonemap_mode = 2 +tonemap_exposure = 0.7 +tonemap_white = 1.84 +glow_enabled = true +glow_intensity = 0.22 +glow_bloom = 0.22 +glow_hdr_threshold = 0.79 +glow_hdr_scale = 0.0 +glow_hdr_luminance_cap = 5.63 +fog_enabled = true +fog_mode = 1 +fog_light_color = Color(0.13725491, 0.39215687, 0.6666667, 1) +fog_density = 0.1831 +fog_aerial_perspective = 0.113 +fog_sky_affect = 0.0 +volumetric_fog_sky_affect = 0.0 +adjustment_enabled = true +adjustment_saturation = 1.3 [node name="Intro" type="Node" unique_id=1801844904] script = ExtResource("1_2nxbv") game_scene_path = "uid://d28cp7a21kwou" -[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=1051527956] -layer = 100 +[node name="Node3D" type="Node3D" parent="." unique_id=1668131521] + +[node name="Camera3D" type="Camera3D" parent="Node3D" unique_id=2070854508] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="Node3D" unique_id=115692868] +environment = SubResource("Environment_mi20s") diff --git a/stages/terrain/region/region.tscn b/stages/terrain/region/region.tscn index cab6157..6d22dd0 100644 --- a/stages/terrain/region/region.tscn +++ b/stages/terrain/region/region.tscn @@ -5,8 +5,8 @@ [ext_resource type="PackedScene" uid="uid://yk78ubpu5ghq" path="res://gui/game/pass_day/pass_day.tscn" id="3_ktnx3"] [ext_resource type="PackedScene" uid="uid://12nak7amd1uq" path="res://gui/game/game_gui.tscn" id="4_qdnee"] [ext_resource type="PackedScene" uid="uid://bgvbgeq46wee2" path="res://entities/player/player.tscn" id="5_ovqi1"] -[ext_resource type="PackedScene" uid="uid://cg1visg52i21a" path="res://entities/interactables/ladder/ladder.tscn" id="6_2w03p"] [ext_resource type="PackedScene" uid="uid://d324mlmgls4fs" path="res://entities/interactables/truck/recharge/truck_recharge.tscn" id="7_6d8m3"] +[ext_resource type="PackedScene" uid="uid://b8m537op75gib" path="res://entities/interactables/door/door.tscn" id="8_2f6js"] [ext_resource type="PackedScene" uid="uid://dj7gp3crtg2yt" path="res://entities/camera/camera.tscn" id="8_fwgig"] [node name="Region" type="Node2D" unique_id=1509166288 node_paths=PackedStringArray("entity_container")] @@ -28,14 +28,31 @@ region = NodePath("../..") [node name="Entities" type="Node2D" parent="." unique_id=2132324579] y_sort_enabled = true -[node name="TruckLadder" parent="Entities" unique_id=1990299618 instance=ExtResource("6_2w03p")] -position = Vector2(51, -112) - [node name="Player" parent="Entities" unique_id=75851644 instance=ExtResource("5_ovqi1")] z_index = 1 +position = Vector2(3000, -41) [node name="TruckRecharge" parent="Entities" unique_id=2068738444 instance=ExtResource("7_6d8m3")] -position = Vector2(-50, -124) +position = Vector2(-1, -169) + +[node name="AstraDoor" parent="Entities" unique_id=2053096538 instance=ExtResource("8_2f6js")] +unique_name_in_owner = true +visible = false +modulate = Color(1, 0, 0, 1) +position = Vector2(-43, 367) +available = false +default_info_title = "ASTRA_FACTORY" +default_info_desc = "ASTRA_FACTORY_TEXT" + +[node name="ShipGarageDoor" parent="Entities" unique_id=1073871193 instance=ExtResource("8_2f6js")] +unique_name_in_owner = true +visible = false +modulate = Color(1, 0, 0, 1) +to_scene_id = "GARAGE" +default_interact_text = "ENTER" +available = false +default_info_title = "MYSTERIOUS_DOOR" +default_info_desc = "MYSTERIOUS_DOOR_TEXT" [node name="Camera" parent="." unique_id=1399042986 node_paths=PackedStringArray("following") instance=ExtResource("8_fwgig")] following = NodePath("../Entities/Player") diff --git a/stages/terrain/region/scripts/region.gd b/stages/terrain/region/scripts/region.gd index 7834be1..ae9aa18 100644 --- a/stages/terrain/region/scripts/region.gd +++ b/stages/terrain/region/scripts/region.gd @@ -9,6 +9,7 @@ const TILE_SET : TileSet = preload("res://stages/terrain/region/resources/moss_b const TILE_SCALE = 1 const TILE_SIZE : int = roundi(TILE_SET.tile_size.x * TILE_SCALE) const START_ROCK_HOLE_RADIUS = 5 +const PLAYER_ROCK_HOLE_RADIUS = 5 const START_DECONTAMINATION_HOLE_RADIUS = 3 const CHUNK_TILE_SIZE : int = 20 const CHUNK_SIZE = CHUNK_TILE_SIZE * TILE_SIZE @@ -69,8 +70,6 @@ func _ready(): if e is Plant: data.add_plant_data(e.data, false) - generate_first_entities() - ground_layer = GroundLayer.new(self) add_child(ground_layer) rock_layer = RockLayer.new(self) @@ -91,11 +90,6 @@ func _process(_d): #region ------------------ Generation ------------------ -func generate_first_entities(): - if not (Vector2i.ZERO in data.generated_chunk_entities): - # Generate shovel - drop_item(Shovel.new(), entity_container.global_position + Vector2(0, 100)) - func get_chunk_key(coord) -> String: return "%d:%d" % [coord.x, coord.y] @@ -150,6 +144,21 @@ func edit_map_origin(): rock_layer.remove_rocks(hole_tiles, true) decontamination_layer.place_decontaminations(decontamination_tiles, true) + # Dig a hole in player position + var player_hole_tiles : Array[Vector2i] = [] + var player_tile_position := Vector2i( + roundi(data.player_position.x/float(TILE_SIZE)), + roundi(data.player_position.y/float(TILE_SIZE)) + ) + for x in range(-PLAYER_ROCK_HOLE_RADIUS, PLAYER_ROCK_HOLE_RADIUS): + for y in range(-PLAYER_ROCK_HOLE_RADIUS, PLAYER_ROCK_HOLE_RADIUS): + var coord = Vector2i(x,y) + if coord.distance_to(Vector2.ZERO) < PLAYER_ROCK_HOLE_RADIUS: + player_hole_tiles.append(coord + player_tile_position) + rock_layer.remove_rocks(player_hole_tiles, true) + + setup_tutorial_doors() + func remove_chunk(chunk : Chunk): generated_chunks.erase(get_chunk_key(chunk.data.chunk_coord)) chunk.unload() @@ -179,6 +188,18 @@ func save(): data.player_position = player.global_position GameInfo.save_game_data() +func setup_tutorial_doors(): + %AstraDoor.visible = data.tutorial + %ShipGarageDoor.visible = data.tutorial + if data.tutorial: + %AstraDoor.global_position = data.get_random_spawn_position() + %AstraDoor.available = false + %ShipGarageDoor.available = data.state == RegionData.State.SUCCEEDED + data.succeded.connect( + func (): + %ShipGarageDoor.available = true + ) + #endregion #region ------------------ Usage ------------------ diff --git a/stages/terrain/region/scripts/region_data.gd b/stages/terrain/region/scripts/region_data.gd index ea3f0c4..b6aae68 100644 --- a/stages/terrain/region/scripts/region_data.gd +++ b/stages/terrain/region/scripts/region_data.gd @@ -14,6 +14,7 @@ signal pass_day_ended(region_data : RegionData) const DEFAULT_START_CHARGE := 10 const DEFAULT_OBJECTIVE := 10 +const MAX_RANDOM_SPAWN_DISTANCE = 3000 @export var region_seed : int @export var region_name : String @@ -31,7 +32,7 @@ const DEFAULT_OBJECTIVE := 10 @export var chunks_data : Dictionary[String, ChunkData] -@export var player_position : Vector2i = Region.CHUNK_SIZE/2. * Vector2.ONE +@export var player_position : Vector2i = Region.CHUNK_SIZE/2. * Vector2.ONE + get_random_spawn_position() @export var charges : int : set(v): @@ -81,7 +82,9 @@ func add_chunk_data(coord : Vector2i, data : ChunkData): chunks_data[get_coord_id(coord)] = data func get_chunk_data(coord : Vector2i) -> ChunkData: - return chunks_data[get_coord_id(coord)] + if get_coord_id(coord) in chunks_data: + return chunks_data[get_coord_id(coord)] + return null func get_or_create_chunk_data(coord : Vector2i) -> ChunkData: if has_chunk_data(coord): @@ -147,3 +150,13 @@ func _on_plant_disappeared(plant_data : PlantData): update() #endregion + + +func get_random_spawn_position(): + var rng := RandomNumberGenerator.new() + rng.seed = region_seed + + return Vector2( + rng.randf_range(-MAX_RANDOM_SPAWN_DISTANCE,MAX_RANDOM_SPAWN_DISTANCE), + rng.randf_range(-MAX_RANDOM_SPAWN_DISTANCE,MAX_RANDOM_SPAWN_DISTANCE), + ) diff --git a/stages/terrain/region/scripts/tile_map_layers/decontamination_layer.gd b/stages/terrain/region/scripts/tile_map_layers/decontamination_layer.gd index bc56f98..2bff6bd 100644 --- a/stages/terrain/region/scripts/tile_map_layers/decontamination_layer.gd +++ b/stages/terrain/region/scripts/tile_map_layers/decontamination_layer.gd @@ -25,7 +25,7 @@ func place_decontaminations(coords : Array[Vector2i], save := false, on_finished floori(coord.y / float(Region.CHUNK_TILE_SIZE)), ) (region.data - .get_chunk_data(chunk_coord) + .get_or_create_chunk_data(chunk_coord) .update_decontamination_tile_diff(coord, ChunkData.TileDiff.PRESENT)) func is_decontamined(coord : Vector2i) -> bool: diff --git a/stages/terrain/region/scripts/tile_map_layers/rock_layer.gd b/stages/terrain/region/scripts/tile_map_layers/rock_layer.gd index eaf720e..6a131fb 100644 --- a/stages/terrain/region/scripts/tile_map_layers/rock_layer.gd +++ b/stages/terrain/region/scripts/tile_map_layers/rock_layer.gd @@ -37,7 +37,7 @@ func remove_rocks(coords : Array[Vector2i], save = false,on_finished : Callable ) var chunk_tile_coord : Vector2i = coord - chunk_coord * Region.CHUNK_TILE_SIZE (region.data - .get_chunk_data(chunk_coord) + .get_or_create_chunk_data(chunk_coord) .update_rock_tile_diff(chunk_tile_coord, ChunkData.TileDiff.ABSENT)) func dig_rocks(coords : Array[Vector2i]) -> bool: diff --git a/translation/game/gui.csv b/translation/game/gui.csv index c8ffa8d..adc36c9 100644 --- a/translation/game/gui.csv +++ b/translation/game/gui.csv @@ -89,6 +89,9 @@ ONE_TIME_USE,Single use,Usage unique BUILD_%s,Build %s,Construit %s FORK,Fork,Fourche FORK_DESC_TEXT,"Use it to [b]harvest mature plants[/b].","Utilise-la pour [b]récolter les plantes mature[/b]." +DETECTOR,Detector,Détecteur +DETECTOR_DESC_TEXT,"Indicate [b]near signals[/b].","Indique les [b]signaux proches[/b]" +DETECT_USE_TEXT,"Search near signals","Rechercher les signaux proches" HARVEST,Harvest,Récolter KNIFE,Knife,Couteau KNIFE_DESC_TEXT,"Use it to [b]harvest mature plants[/b]. Does not consume energy.",Utilise-le pour [b]récolter les plantes mature[/b]. Ne consomme pas d’énergie. @@ -115,7 +118,7 @@ SCORE_%d,Score %d,Score %d SOLAR_PANNEL,Solar panel,Panneau solaire SOLAR_PANNEL_DESCRIPTION_TEXT,Grants energy when charged. Take several days to recharge,Donne de l’énergie quand chargé. Prend plusieurs jours à se recharger TRUCK_ENTRANCE,Truck entrance,Entrée du camion -ENTER_TRUCK,Enter truck,Entrer dans le camion +ENTER,Enter,Entrer EXIT,Exit,Sortie EXIT_TRUCK,Exit truck,Sortir du camion LADDER_DESC_TEXT,A good old ladder,Une bonne vieille échelle @@ -197,4 +200,8 @@ MUSIC_SOUND_DESIGN_AND_WRITING,"Music, Sound design and Wrinting","Musique, Sons COMMUNICATION,"Communication","Communication" SPLASH_ART,Splash Art,Splash Art TRAILER,"Trailer","Trailer" -CREDITS,Credits,Crédits \ No newline at end of file +CREDITS,Credits,Crédits +ASTRA_FACTORY,Astra Factory,Usine Astra +ASTRA_FACTORY_TEXT,Production factory of Astra base,Usine de production de la base Astra +MYSTERIOUS_DOOR,Mysterious Door,Porte mystérieuse +MYSTERIOUS_DOOR_TEXT,"This door has a space ship logo on it... What could it be ?","Cette porte à un logo de vaisseau marqué dessus... Que peut-il y avoir à l'intérieur ?" \ No newline at end of file