diff --git a/Assets/Animations/Portrait_base_anim.tres b/Assets/Animations/Portrait_base_anim.tres index 3846680..ba08bdb 100644 --- a/Assets/Animations/Portrait_base_anim.tres +++ b/Assets/Animations/Portrait_base_anim.tres @@ -1,8 +1,8 @@ [gd_resource type="SpriteFrames" load_steps=4 format=3 uid="uid://b5l6qnwf46vyd"] -[ext_resource type="Texture2D" uid="uid://brrexw4jtnjag" path="res://addons/Suada/Assets/Sprites/portrait_base_idle3.png" id="1"] -[ext_resource type="Texture2D" uid="uid://ctrxjhqyqjsu3" path="res://addons/Suada/Assets/Sprites/portrait_base_idle2.png" id="2"] -[ext_resource type="Texture2D" uid="uid://cgq3ulsyuh0ja" path="res://addons/Suada/Assets/Sprites/portrait_base_idle1.png" id="3"] +[ext_resource type="Texture2D" uid="uid://brrexw4jtnjag" path="res://addons/Suada_bk/Assets/Sprites/portrait_base_idle3.png" id="1"] +[ext_resource type="Texture2D" uid="uid://ctrxjhqyqjsu3" path="res://addons/Suada_bk/Assets/Sprites/portrait_base_idle2.png" id="2"] +[ext_resource type="Texture2D" uid="uid://cgq3ulsyuh0ja" path="res://addons/Suada_bk/Assets/Sprites/portrait_base_idle1.png" id="3"] [resource] animations = [{ diff --git a/Assets/Fonts/FSEX302.ttf.import b/Assets/Fonts/FSEX302.ttf.import index 3a04b16..6247651 100644 --- a/Assets/Fonts/FSEX302.ttf.import +++ b/Assets/Fonts/FSEX302.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/FSEX302.ttf-cc9c15cb9d32cbd7c3aa3009757748a2. Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 @@ -27,7 +28,12 @@ Fallbacks=null fallbacks=[] Compress=null compress=true -preload=[] +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0) +}] language_support={} script_support={} opentype_features={} diff --git a/Assets/Fonts/FixedsysExcelsior.tres b/Assets/Fonts/FixedsysExcelsior.tres index 7e6289f..fe5c3b6 100644 --- a/Assets/Fonts/FixedsysExcelsior.tres +++ b/Assets/Fonts/FixedsysExcelsior.tres @@ -1,12 +1,9 @@ [gd_resource type="FontFile" load_steps=2 format=3 uid="uid://do40he6kkb8dk"] -[ext_resource type="FontFile" uid="uid://bq0g08pyvyx4p" path="res://Assets/Fonts/FSEX302.ttf" id="1"] +[ext_resource type="FontFile" uid="uid://spyvb3ij26jf" path="res://addons/Suada_bk/Assets/Fonts/FSEX302.ttf" id="1"] [resource] fallbacks = Array[Font]([ExtResource("1")]) -face_index = null -embolden = null -transform = null cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 diff --git a/Assets/Themes/main_theme.tres b/Assets/Themes/main_theme.tres index 68e377b..bfc1654 100644 --- a/Assets/Themes/main_theme.tres +++ b/Assets/Themes/main_theme.tres @@ -1,7 +1,7 @@ [gd_resource type="Theme" load_steps=4 format=3 uid="uid://bruovco3l4r7k"] [ext_resource type="FontFile" uid="uid://do40he6kkb8dk" path="res://addons/Suada/Assets/Fonts/FixedsysExcelsior.tres" id="1"] -[ext_resource type="Texture2D" uid="uid://b03mcowynm1j4" path="res://addons/Suada/Assets/Sprites/dialogue_box_9_tile.png" id="2"] +[ext_resource type="Texture2D" uid="uid://b03mcowynm1j4" path="res://addons/Suada_bk/Assets/Sprites/dialogue_box_9_tile.png" id="2"] [sub_resource type="StyleBoxTexture" id="1"] texture = ExtResource("2") diff --git a/Assets/Themes/name_theme.tres b/Assets/Themes/name_theme.tres index 04f701e..526d6d7 100644 --- a/Assets/Themes/name_theme.tres +++ b/Assets/Themes/name_theme.tres @@ -1,7 +1,7 @@ [gd_resource type="Theme" load_steps=4 format=3 uid="uid://ddetutsupn7m0"] [ext_resource type="FontFile" uid="uid://do40he6kkb8dk" path="res://addons/Suada/Assets/Fonts/FixedsysExcelsior.tres" id="1"] -[ext_resource type="Texture2D" uid="uid://csr38a60osub6" path="res://addons/Suada/Assets/Sprites/name_box_9_tile.png" id="2"] +[ext_resource type="Texture2D" uid="uid://csr38a60osub6" path="res://addons/Suada_bk/Assets/Sprites/name_box_9_tile.png" id="2"] [sub_resource type="StyleBoxTexture" id="2"] texture = ExtResource("2") diff --git a/Nodes/Dialogue.tscn b/Nodes/Dialogue.tscn index f72fe28..933196f 100644 --- a/Nodes/Dialogue.tscn +++ b/Nodes/Dialogue.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=24 format=3 uid="uid://x6mnw54gmo8v"] +[gd_scene load_steps=24 format=3 uid="uid://besgwi2vkunl3"] [ext_resource type="Script" path="res://addons/Suada/Nodes/DialogueSystem.gd" id="1"] [ext_resource type="Theme" uid="uid://ddetutsupn7m0" path="res://addons/Suada/Assets/Themes/name_theme.tres" id="2"] diff --git a/Nodes/DialogueAudioPlayer.gd b/Nodes/DialogueAudioPlayer.gd index d34b5a9..13ba5cd 100644 --- a/Nodes/DialogueAudioPlayer.gd +++ b/Nodes/DialogueAudioPlayer.gd @@ -1,3 +1,4 @@ +class_name DialogueAudioPlayer extends AudioStreamPlayer2D @@ -13,7 +14,7 @@ func set_voice_sound(audio: AudioStream): ## @param wait_to_play If true, waits until the last sound was played. func play_voice_sound(wait_to_play: bool = true): if wait_to_play: - if !self.playing: - self.play() + if !playing: + play() else: - self.play() + play() diff --git a/Nodes/DialogueAudioPlayer.gd.uid b/Nodes/DialogueAudioPlayer.gd.uid new file mode 100644 index 0000000..7641d07 --- /dev/null +++ b/Nodes/DialogueAudioPlayer.gd.uid @@ -0,0 +1 @@ +uid://bisrw25bck2i6 diff --git a/Nodes/DialogueBox.gd b/Nodes/DialogueBox.gd index 57f79d2..42ee6f0 100644 --- a/Nodes/DialogueBox.gd +++ b/Nodes/DialogueBox.gd @@ -2,6 +2,8 @@ class_name DialogueTextBox extends Control var _text_next_char_it: float = 0 +var _parsed_dialog: ParsedDialog = null +var _pause: bool = false @onready var _text_label = $Panel/Text as RichTextLabel @onready var _portrait_box: PortraitBox = $"../PortraitBox" @@ -9,29 +11,33 @@ var _text_next_char_it: float = 0 func _ready(): - if SuadaGlobals.text_speed == -1: + if SuadaConfig.text_speed == -1: _text_label.visible_characters = -1 func _process(_delta): - if _text_label.text.is_empty(): + if _text_label.text.is_empty() or !_text_label.visible: return if _text_label.visible_ratio == 1: _finished_effect.visible = true return - var text = _text_label.text + if _pause: + return _text_next_char_it = ( - SuadaGlobals.text_speed + SuadaConfig.text_speed if _text_next_char_it >= 1 - else _text_next_char_it + SuadaGlobals.text_speed + else _text_next_char_it + SuadaConfig.text_speed ) _text_label.visible_characters += _text_next_char_it - var letter = text[_text_label.visible_characters - 1] - if SuadaGlobals.vocal_sound: + var letter = _text_label.text[_text_label.visible_characters - 1].to_lower() + _handle_effects(_text_label.visible_characters as int) + _handle_letter_pauses(letter) + + if SuadaConfig.vocal_sound: if letter == "a" or letter == "e" or letter == "i" or letter == "o" or letter == "u": _portrait_box.play_voice() else: @@ -39,7 +45,11 @@ func _process(_delta): func set_text(text: String, font: Font, colour: Color = Color.WHITE, visible: bool = false) -> void: + var text_without_suada_effect = BBCParser.bbc_remover(text, BBCCodes.BBC_REXP) _text_label.text = text + _parsed_dialog = BBCParser.parse(_text_label.get_parsed_text()) + _text_label.text = text_without_suada_effect + _text_label.add_theme_font_override("normal_font", font) _text_label.add_theme_color_override("default_color", colour) _text_label.visible_characters = -1 if visible else 0 @@ -51,3 +61,33 @@ func show_all_text(): func is_complete() -> bool: return _text_label.visible_ratio == 1 + + +func _handle_effects(pos: int): + if not _parsed_dialog.effects.has(pos): + return + + var curr_effect: EffectLocation = _parsed_dialog.effects[pos] + _parsed_dialog.effects.erase(pos) + + match curr_effect.effect: + BBCCodes.BBC_EFFECT.WAIT: + _handle_pause(curr_effect.value.to_int()) + _: + pass + + +func _handle_letter_pauses(letter: String): + match letter: + ",", ".": + _handle_pause(0.1) + "?", "!": + _handle_pause(0.2) + _: + pass + + +func _handle_pause(time: float) -> void: + _pause = true + await get_tree().create_timer(time).timeout + _pause = false diff --git a/Nodes/DialogueBox.gd.uid b/Nodes/DialogueBox.gd.uid new file mode 100644 index 0000000..14febcf --- /dev/null +++ b/Nodes/DialogueBox.gd.uid @@ -0,0 +1 @@ +uid://dxciertla70xt diff --git a/Nodes/DialogueSystem.gd b/Nodes/DialogueSystem.gd index 0bc3ef8..a77f183 100644 --- a/Nodes/DialogueSystem.gd +++ b/Nodes/DialogueSystem.gd @@ -1,8 +1,6 @@ +class_name DialogueSystem extends Control -# Imports -const Types = preload("res://addons/Suada/Nodes/Objects/Types.gd") - # Customise (FOR USER) @export var _voice_snd_effect: AudioStream = load("res://addons/Suada/Assets/Sounds/vocal_sound_1.ogg") @@ -26,7 +24,7 @@ var _pause = false var _exiting = false var _chosen = false var _choice = 0 -var _conversation: Array[Dialog] = [] +var _conversation: Dictionary[int, Dialog] = {} @onready var _dialogue_box: DialogueTextBox = $DialogueContainer/TextBox @onready var _name_box: MarginContainer = $NameBox @@ -35,7 +33,7 @@ var _conversation: Array[Dialog] = [] ## Add a convesation and setup the dialog system. -func add_conversation(conversation: Array) -> void: +func add_conversation(conversation: Dictionary[int, Dialog]) -> void: _conversation = conversation _setup() @@ -65,8 +63,8 @@ func _process(_delta): # Interact behaviour if _interact_key_pressed: if _conversation[_page].type == Types.DialogType.NORMAL: - if _page + 1 < _conversation.size(): - _page += 1 + _page = _conversation[_page].next + if _page != -1: _set_portrait(_conversation[_page].portrait) _setup() else: @@ -109,12 +107,21 @@ func _input(event): _down_key_pressed = 0 +func _reset() -> void: + _choice = 0 + _chosen = false + _pause = false + + func _setup() -> void: + _reset() + _portrait_box.setup( load(_conversation[0].portrait.portrait_path) as SpriteFrames, _voice_snd_effect ) if _page < 0 or _page >= _conversation.size(): + printerr("Wrong conversation position.") return var name_str = _conversation[_page].name @@ -134,28 +141,23 @@ func _setup() -> void: func _handle_dialogue_choice() -> void: _handle_pause(0.1) - if _page + 1 < _conversation.size(): - var nl = _next_line[_page] - match nl[_choice]: - -1: - queue_free() - return - 0: - _page += 1 - _: - _page = nl[_choice] - - _setup() - else: + if _choice > _conversation.size(): + printerr("Choice position out of conversation.") queue_free() + return - _chosen = false + if _choice == -1: + queue_free() + return + + _page = _conversation[_page].choices[_choice].next + _setup() func _handle_dialogue_change_choice(visible: bool = false) -> void: var text = _conversation[_page].text for choice_index in _conversation[_page].choices.size(): - var choice_text = _conversation[_page].choices[choice_index] + var choice_text = _conversation[_page].choices[choice_index].text text += "\n" diff --git a/Nodes/DialogueSystem.gd.uid b/Nodes/DialogueSystem.gd.uid new file mode 100644 index 0000000..7d5c709 --- /dev/null +++ b/Nodes/DialogueSystem.gd.uid @@ -0,0 +1 @@ +uid://7u5tl70iop21 diff --git a/Nodes/Effects/RichTextEffectFlicker.gd.uid b/Nodes/Effects/RichTextEffectFlicker.gd.uid new file mode 100644 index 0000000..5759dcd --- /dev/null +++ b/Nodes/Effects/RichTextEffectFlicker.gd.uid @@ -0,0 +1 @@ +uid://5yswxgvifti8 diff --git a/Nodes/Effects/RichTextEffectGhost.gd b/Nodes/Effects/RichTextEffectGhost.gd index 6733d4b..c09c9ea 100644 --- a/Nodes/Effects/RichTextEffectGhost.gd +++ b/Nodes/Effects/RichTextEffectGhost.gd @@ -15,4 +15,5 @@ func _process_custom_fx(char_fx): var alpha = sin(char_fx.elapsed_time * speed + (char_fx.range.x / span)) * 0.5 + 0.5 char_fx.color.a = alpha + return true diff --git a/Nodes/Effects/RichTextEffectGhost.gd.uid b/Nodes/Effects/RichTextEffectGhost.gd.uid new file mode 100644 index 0000000..bb3d0b9 --- /dev/null +++ b/Nodes/Effects/RichTextEffectGhost.gd.uid @@ -0,0 +1 @@ +uid://blrkxba5rjrdd diff --git a/Nodes/Effects/RichTextEffectMatrix.gd.uid b/Nodes/Effects/RichTextEffectMatrix.gd.uid new file mode 100644 index 0000000..53a5c76 --- /dev/null +++ b/Nodes/Effects/RichTextEffectMatrix.gd.uid @@ -0,0 +1 @@ +uid://cojx35wpxirrm diff --git a/Nodes/Effects/RichTextEffectPulse.gd.uid b/Nodes/Effects/RichTextEffectPulse.gd.uid new file mode 100644 index 0000000..f60a465 --- /dev/null +++ b/Nodes/Effects/RichTextEffectPulse.gd.uid @@ -0,0 +1 @@ +uid://biw6a0o6espjc diff --git a/Nodes/Effects/RichTextEffectShift.gd.uid b/Nodes/Effects/RichTextEffectShift.gd.uid new file mode 100644 index 0000000..2f453c3 --- /dev/null +++ b/Nodes/Effects/RichTextEffectShift.gd.uid @@ -0,0 +1 @@ +uid://drmvnbvsiehsq diff --git a/Nodes/Effects/RichTextEffectSpin.gd b/Nodes/Effects/RichTextEffectSpin.gd new file mode 100755 index 0000000..9c6794c --- /dev/null +++ b/Nodes/Effects/RichTextEffectSpin.gd @@ -0,0 +1,19 @@ +@tool +class_name RichTextEffectSpin +extends RichTextEffect + +# Syntax: [spin freq=5.0 span=10.0][/spin] + +# Define the tag name. +var bbcode = "spin" + + +func _process_custom_fx(char_fx: CharFXTransform): + var freq = char_fx.env.get("freq", 5.0) + + var val: float = char_fx.elapsed_time * 100 + var shift: float = sin(val * PI * freq / SuadaGlobals.TARGET_FPS) + var rotation = shift / 4 + char_fx.transform = char_fx.transform.rotated_local(rotation) + + return true diff --git a/Nodes/Effects/RichTextEffectSpin.gd.uid b/Nodes/Effects/RichTextEffectSpin.gd.uid new file mode 100644 index 0000000..cf0fedf --- /dev/null +++ b/Nodes/Effects/RichTextEffectSpin.gd.uid @@ -0,0 +1 @@ +uid://bbvp37cw5mk81 diff --git a/Nodes/FinishEffect.gd.uid b/Nodes/FinishEffect.gd.uid new file mode 100644 index 0000000..d9bd9c7 --- /dev/null +++ b/Nodes/FinishEffect.gd.uid @@ -0,0 +1 @@ +uid://cvnq24ncd7wdb diff --git a/Nodes/Globals/BBCCodes.gd b/Nodes/Globals/BBCCodes.gd new file mode 100644 index 0000000..ceb4196 --- /dev/null +++ b/Nodes/Globals/BBCCodes.gd @@ -0,0 +1,17 @@ +class_name BBCCodes + +## Dialog types. +enum BBC_EFFECT { NONE, WAIT } + +# BBCode effects. +const BBC_CODES_MAP: Dictionary[String, BBC_EFFECT] = { + "wait": BBC_EFFECT.WAIT, +} + +# BBCode regular expression. +const BBC_REXP: String = "\\[(wait)(?:=([\\w\\d]+))?\\]" + +# Any BBCode regular expression. +# Used in case a wrong one was read and we need to remove it. +# It matches any BBCode, even not supported. +const BBC_ANY_REXP: String = "\\[.*\\]" diff --git a/Nodes/Globals/BBCCodes.gd.uid b/Nodes/Globals/BBCCodes.gd.uid new file mode 100644 index 0000000..c5bfe2f --- /dev/null +++ b/Nodes/Globals/BBCCodes.gd.uid @@ -0,0 +1 @@ +uid://d2v6wmxmtm5xl diff --git a/Nodes/Globals/SuadaConfig.gd b/Nodes/Globals/SuadaConfig.gd new file mode 100644 index 0000000..ad94e21 --- /dev/null +++ b/Nodes/Globals/SuadaConfig.gd @@ -0,0 +1,8 @@ +extends Node + +# Configurable +@export var text_speed: float = 0.25 +@export var vocal_sound: bool = true + +# Counters +@export var effect_cnt: int = 0 diff --git a/Nodes/Globals/SuadaConfig.gd.uid b/Nodes/Globals/SuadaConfig.gd.uid new file mode 100644 index 0000000..4251568 --- /dev/null +++ b/Nodes/Globals/SuadaConfig.gd.uid @@ -0,0 +1 @@ +uid://fuj82s32vfds diff --git a/Nodes/Globals/SuadaGlobals.gd b/Nodes/Globals/SuadaGlobals.gd new file mode 100644 index 0000000..59a7eaa --- /dev/null +++ b/Nodes/Globals/SuadaGlobals.gd @@ -0,0 +1,8 @@ +class_name SuadaGlobals +extends Resource + +const TARGET_FPS: int = 60 + +# Effect variables +const AMPLITUDE: int = 4 +const FREQUENCY: int = 2 diff --git a/Nodes/Globals/SuadaGlobals.gd.uid b/Nodes/Globals/SuadaGlobals.gd.uid new file mode 100644 index 0000000..9533c67 --- /dev/null +++ b/Nodes/Globals/SuadaGlobals.gd.uid @@ -0,0 +1 @@ +uid://chogjltrgtqcu diff --git a/Nodes/Globals/Types.gd b/Nodes/Globals/Types.gd new file mode 100644 index 0000000..3a818a5 --- /dev/null +++ b/Nodes/Globals/Types.gd @@ -0,0 +1,4 @@ +class_name Types + +## Dialog types. +enum DialogType { NORMAL, CHOICE } diff --git a/Nodes/Globals/Types.gd.uid b/Nodes/Globals/Types.gd.uid new file mode 100644 index 0000000..d77b21b --- /dev/null +++ b/Nodes/Globals/Types.gd.uid @@ -0,0 +1 @@ +uid://de78qnqcg336r diff --git a/Nodes/NameBox.gd.uid b/Nodes/NameBox.gd.uid new file mode 100644 index 0000000..090418c --- /dev/null +++ b/Nodes/NameBox.gd.uid @@ -0,0 +1 @@ +uid://c6du80pgr26nq diff --git a/Nodes/NameText.gd.uid b/Nodes/NameText.gd.uid new file mode 100644 index 0000000..6cf7f3d --- /dev/null +++ b/Nodes/NameText.gd.uid @@ -0,0 +1 @@ +uid://dt3sbbh26vbd8 diff --git a/Nodes/Objects/Choice.gd b/Nodes/Objects/Choice.gd new file mode 100644 index 0000000..3b3d455 --- /dev/null +++ b/Nodes/Objects/Choice.gd @@ -0,0 +1,15 @@ +## The Choice class +## +## @desc: +## An object to hold choice data. +class_name Choice +extends Resource + +var text: String = "" +var next: int = -1 + + +## Initialise the dialog object. +func _init(text: String, next: int = -1): + self.text = text + self.next = next diff --git a/Nodes/Objects/Choice.gd.uid b/Nodes/Objects/Choice.gd.uid new file mode 100644 index 0000000..048f2a1 --- /dev/null +++ b/Nodes/Objects/Choice.gd.uid @@ -0,0 +1 @@ +uid://cpo7d21ps1epl diff --git a/Nodes/Objects/Conversation.gd b/Nodes/Objects/Conversation.gd new file mode 100644 index 0000000..d088929 --- /dev/null +++ b/Nodes/Objects/Conversation.gd @@ -0,0 +1,16 @@ +## The Conversation class +## +## @desc: +## An object to hold conversation data. +class_name Conversation +extends Resource + +var dialogs: Array[Dialog] = [] +var length: int = 0: + get: + return dialogs.size() + + +## Initialise the dialog object. +func _init(dialogs: Array[Dialog]): + self.dialogs = dialogs diff --git a/Nodes/Objects/Conversation.gd.uid b/Nodes/Objects/Conversation.gd.uid new file mode 100644 index 0000000..fdbe699 --- /dev/null +++ b/Nodes/Objects/Conversation.gd.uid @@ -0,0 +1 @@ +uid://ct8xbkhwgnctf diff --git a/Nodes/Objects/Dialog.gd b/Nodes/Objects/Dialog.gd index faefd73..e6388b6 100644 --- a/Nodes/Objects/Dialog.gd +++ b/Nodes/Objects/Dialog.gd @@ -3,34 +3,28 @@ ## @desc: ## An object to hold dialog data. class_name Dialog +extends Resource -const Types = preload("res://addons/Suada/Nodes/Objects/Types.gd") - -var text: String = "": - get: - return text - -var name: String = "": - get: - return name - -var portrait: Portrait = null: - get: - return portrait - -var type: int = Types.DialogType.NORMAL: - get: - return type - -var choices: Array[String] = []: - get: - return choices +var id: int = -1 +var text: String = "" +var name: String = "" +var portrait: Portrait = null +var type: int = Types.DialogType.NORMAL +var choices: Array[Choice] = [] +var next: int = -1 ## Initialise the dialog object. func _init( - text: String, name: String, portrait_path: String, type: int, choices: Array[String] = [] + id: int, + text: String, + name: String, + portrait_path: String, + type: int, + next = -1, + choices: Array[Choice] = [] ): + self.id = id self.text = text self.name = name @@ -38,4 +32,5 @@ func _init( portrait = Portrait.new(portrait_path) self.type = type + self.next = next self.choices = choices diff --git a/Nodes/Objects/Dialog.gd.uid b/Nodes/Objects/Dialog.gd.uid new file mode 100644 index 0000000..4a548da --- /dev/null +++ b/Nodes/Objects/Dialog.gd.uid @@ -0,0 +1 @@ +uid://dckc4tohfbn2o diff --git a/Nodes/Objects/EffectLocation.gd b/Nodes/Objects/EffectLocation.gd new file mode 100644 index 0000000..a1c4fc1 --- /dev/null +++ b/Nodes/Objects/EffectLocation.gd @@ -0,0 +1,13 @@ +## Effect location class. +## +## Holds the position and effect. +class_name EffectLocation +extends Resource + +var effect: BBCCodes.BBC_EFFECT = BBCCodes.BBC_EFFECT.NONE +var value: String + + +func _init(ef: BBCCodes.BBC_EFFECT, val: String): + effect = ef + value = val diff --git a/Nodes/Objects/EffectLocation.gd.uid b/Nodes/Objects/EffectLocation.gd.uid new file mode 100644 index 0000000..20a119e --- /dev/null +++ b/Nodes/Objects/EffectLocation.gd.uid @@ -0,0 +1 @@ +uid://dto5arm6t7gsf diff --git a/Nodes/Objects/ParsedDialog.gd b/Nodes/Objects/ParsedDialog.gd new file mode 100644 index 0000000..16be926 --- /dev/null +++ b/Nodes/Objects/ParsedDialog.gd @@ -0,0 +1,21 @@ +## Parse return class. +## +## Holds the parsed text data. +class_name ParsedDialog +extends Resource + +var orig_text: String = "" +var text: String = "" +var effects: Dictionary[int, EffectLocation] = {} + +var text_len: int: + get: + return text.length() + +var effects_len: int: + get: + return effects.size() + + +func _init(text: String): + orig_text = text diff --git a/Nodes/Objects/ParsedDialog.gd.uid b/Nodes/Objects/ParsedDialog.gd.uid new file mode 100644 index 0000000..92d7aec --- /dev/null +++ b/Nodes/Objects/ParsedDialog.gd.uid @@ -0,0 +1 @@ +uid://cv3o4rkddxjef diff --git a/Nodes/Objects/Portrait.gd b/Nodes/Objects/Portrait.gd index 0b18493..e111383 100644 --- a/Nodes/Objects/Portrait.gd +++ b/Nodes/Objects/Portrait.gd @@ -6,18 +6,11 @@ ## so it triggers the animation in a random time number between the range. ## class_name Portrait +extends Resource -var portrait_path: String = "": - get: - return portrait_path - -var animation_trigger: int = 120: - get: - return animation_trigger - -var animation_trigger_range: Vector2 = Vector2(100, 200): - get: - return animation_trigger_range +var portrait_path: String = "" +var animation_trigger: int = 120 +var animation_trigger_range: Vector2 = Vector2(100, 200) func _init( diff --git a/Nodes/Objects/Portrait.gd.uid b/Nodes/Objects/Portrait.gd.uid new file mode 100644 index 0000000..13d7702 --- /dev/null +++ b/Nodes/Objects/Portrait.gd.uid @@ -0,0 +1 @@ +uid://bd6mubgolpn28 diff --git a/Nodes/PortraitBox.gd b/Nodes/PortraitBox.gd index 3ea8022..a826d4c 100644 --- a/Nodes/PortraitBox.gd +++ b/Nodes/PortraitBox.gd @@ -1,8 +1,8 @@ class_name PortraitBox extends Control -@onready var _audio_player: AudioStreamPlayer2D = $"../../AudioPlayer" -@onready var _portrait: AnimatedSprite2D = $Panel/Portrait +@onready var _audio_player: DialogueAudioPlayer = $"../../AudioPlayer" +@onready var _portrait: PortraitFrame = $Panel/Portrait func setup(portrait: SpriteFrames, audio: AudioStream) -> void: @@ -16,6 +16,6 @@ func set_voice_sound(audio: AudioStream) -> void: ## Play the voice sound. ## Pass True to wait for previous sound to finish, otherwise False. -func play_voice(talk_anim: String = "talk", wait_to_play: bool = true) -> void: +func play_voice(talk_anim: String = "talk", wait_to_play: bool = false) -> void: _portrait.play_animation(talk_anim) _audio_player.play_voice_sound(wait_to_play) diff --git a/Nodes/PortraitBox.gd.uid b/Nodes/PortraitBox.gd.uid new file mode 100644 index 0000000..ad2c6a5 --- /dev/null +++ b/Nodes/PortraitBox.gd.uid @@ -0,0 +1 @@ +uid://by5tek2mm1kds diff --git a/Nodes/PortraitSprite.gd b/Nodes/PortraitSprite.gd index 25178fa..445397b 100644 --- a/Nodes/PortraitSprite.gd +++ b/Nodes/PortraitSprite.gd @@ -2,13 +2,13 @@ class_name PortraitFrame extends AnimatedSprite2D ## Portrait states. -const PORTRAIT_STATES: Dictionary = { +const PORTRAIT_STATES: Dictionary[String, int] = { "idle": 0, ## Idle. "talk": 1, ## Talking. } ## Portrait animations names -const PORTRAIT_ANIM_NAMES: Dictionary = { +const PORTRAIT_ANIM_NAMES: Dictionary[String, String] = { "idle": "idle", ## Idle. "talk": "talk", ## Talk } diff --git a/Nodes/PortraitSprite.gd.uid b/Nodes/PortraitSprite.gd.uid new file mode 100644 index 0000000..76bfdca --- /dev/null +++ b/Nodes/PortraitSprite.gd.uid @@ -0,0 +1 @@ +uid://wx5n10oc1vki diff --git a/Nodes/Utils/BBCParser.gd b/Nodes/Utils/BBCParser.gd new file mode 100644 index 0000000..353e3e9 --- /dev/null +++ b/Nodes/Utils/BBCParser.gd @@ -0,0 +1,70 @@ +class_name BBCParser + + +static func _get_value(text: String, pos: int) -> Array: + var value = "" + + while pos < text.length(): + var in_letter: String = text[pos] + + if in_letter != "]" && in_letter != "/" && in_letter != "=": + value += in_letter + elif in_letter == "]": + break + + pos += 1 + + return [value, pos] + + +## Parse a text to get the data to show it on the dialog. +## +## Returns a [ParseReturn] object with the data without BBCodes and a list of effects and colours +## positions. +## +## If there is a wrong or unsupported BBCode, will be removed. +## +## @param text The text to parse. +## @returns A [ParseReturn] object. +static func parse(text: String) -> ParsedDialog: + var parsed_dialog := ParsedDialog.new(text) + + var regex: RegEx = RegEx.new() + regex.compile(BBCCodes.BBC_REXP) + + while regex.search(text): + var regex_match = regex.search(text) + var match_start = regex_match.get_start() + + if ( + regex_match.get_group_count() != 3 + and !BBCCodes.BBC_CODES_MAP.has(regex_match.strings[1]) + ): + printerr("Wrong BBCode format") + return parsed_dialog + + var effect = BBCCodes.BBC_CODES_MAP[regex_match.strings[1]] + var effect_value = regex_match.strings[2] + parsed_dialog.effects[match_start] = EffectLocation.new(effect, effect_value) + + var new_text = text.substr(0, match_start) + var match_end = regex_match.get_end() + new_text += text.substr(match_end) + text = new_text + + return parsed_dialog + + +static func bbc_remover(text: String, regex_str: String) -> String: + var regex := RegEx.new() + regex.compile(regex_str) + + var out = "" + var last_pos = 0 + for regex_match in regex.search_all(text): + var start := regex_match.get_start() + out += text.substr(last_pos, start - last_pos) + last_pos = regex_match.get_end() + + out += text.substr(last_pos) + return out diff --git a/Nodes/Utils/BBCParser.gd.uid b/Nodes/Utils/BBCParser.gd.uid new file mode 100644 index 0000000..ac124d2 --- /dev/null +++ b/Nodes/Utils/BBCParser.gd.uid @@ -0,0 +1 @@ +uid://bjmxbeqsv3wqe diff --git a/README.md b/README.md index 5385065..7325656 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,13 @@ - [License](#license) -# Suada 0.1.1 +# Suada 0.2.0 Dialogue system plugin for Godot 4 -# What's new in 0.1.1? +# What's new in 0.2.0? -- Show dialogue choices. +- Dialogue choices. # Sample diff --git a/Scenes/Dialogue.tscn b/Scenes/Dialogue.tscn new file mode 100644 index 0000000..a4bfdc4 --- /dev/null +++ b/Scenes/Dialogue.tscn @@ -0,0 +1,194 @@ +[gd_scene load_steps=28 format=3 uid="uid://x6mnw54gmo8v"] + +[ext_resource type="Script" uid="uid://7u5tl70iop21" path="res://addons/Suada_bk/Nodes/DialogueSystem.gd" id="1"] +[ext_resource type="Theme" uid="uid://ddetutsupn7m0" path="res://addons/Suada/Assets/Themes/name_theme.tres" id="2"] +[ext_resource type="Theme" uid="uid://bruovco3l4r7k" path="res://addons/Suada/Assets/Themes/main_theme.tres" id="3"] +[ext_resource type="Texture2D" uid="uid://d4d46x70xfeaa" path="res://addons/Suada_bk/Assets/Sprites/indicator.png" id="5"] +[ext_resource type="Script" uid="uid://dxciertla70xt" path="res://addons/Suada_bk/Nodes/DialogueBox.gd" id="8"] +[ext_resource type="Script" uid="uid://by5tek2mm1kds" path="res://addons/Suada_bk/Nodes/PortraitBox.gd" id="9"] +[ext_resource type="SpriteFrames" uid="uid://b5l6qnwf46vyd" path="res://addons/Suada/Assets/Animations/Portrait_base_anim.tres" id="9_rdh7m"] +[ext_resource type="Script" uid="uid://blrkxba5rjrdd" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectGhost.gd" id="9_ydpfn"] +[ext_resource type="Script" uid="uid://bisrw25bck2i6" path="res://addons/Suada_bk/Nodes/DialogueAudioPlayer.gd" id="10"] +[ext_resource type="Script" uid="uid://biw6a0o6espjc" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectPulse.gd" id="10_mlhx6"] +[ext_resource type="Script" uid="uid://cvnq24ncd7wdb" path="res://addons/Suada_bk/Nodes/FinishEffect.gd" id="10_typqo"] +[ext_resource type="Script" uid="uid://wx5n10oc1vki" path="res://addons/Suada_bk/Nodes/PortraitSprite.gd" id="11"] +[ext_resource type="Script" uid="uid://cojx35wpxirrm" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectMatrix.gd" id="11_53617"] +[ext_resource type="Script" uid="uid://5yswxgvifti8" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectFlicker.gd" id="11_bbs34"] +[ext_resource type="Script" uid="uid://bbvp37cw5mk81" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectSpin.gd" id="12_2n2xh"] +[ext_resource type="Script" uid="uid://dt3sbbh26vbd8" path="res://addons/Suada_bk/Nodes/NameText.gd" id="12_mja5r"] +[ext_resource type="Script" uid="uid://drmvnbvsiehsq" path="res://addons/Suada_bk/Nodes/Effects/RichTextEffectShift.gd" id="12_njesn"] + +[sub_resource type="RichTextEffect" id="RichTextEffect_omcs0"] +script = ExtResource("9_ydpfn") + +[sub_resource type="RichTextEffect" id="RichTextEffect_mfy4b"] +script = ExtResource("10_mlhx6") + +[sub_resource type="RichTextEffect" id="RichTextEffect_p8ibk"] +script = ExtResource("11_53617") + +[sub_resource type="RichTextEffect" id="RichTextEffect_i3ro0"] +script = ExtResource("12_njesn") + +[sub_resource type="RichTextEffect" id="RichTextEffect_kkw1g"] +script = ExtResource("11_bbs34") + +[sub_resource type="RichTextEffect" id="RichTextEffect_s5tcp"] +script = ExtResource("12_2n2xh") + +[sub_resource type="GDScript" id="GDScript_ad1ir"] +script/source = "@tool +class_name RichTextWait +extends RichTextEffect + +# Syntax: [wait time=2] + +var bbcode := \"wait\" + + +func _process_custom_fx(char_fx: CharFXTransform) -> bool: + var wait_time: float = char_fx.env.get(\"time\", 1.0) + var speed: float = char_fx.env.get(\"speed\", 16.0) + + if char_fx.elapsed_time <= wait_time: + char_fx.visible = false + return true + + char_fx.visible = false + if char_fx.elapsed_time > ((char_fx.relative_index / speed) + wait_time): + char_fx.visible = true + + return true +" + +[sub_resource type="RichTextEffect" id="RichTextEffect_xw4hv"] +script = SubResource("GDScript_ad1ir") + +[sub_resource type="GDScript" id="GDScript_qmumq"] +script/source = "@tool +class_name RichTextEffectPause +extends RichTextEffect + +var bbcode := \"pause\" +var current_char: int = 0 + +func _process_custom_fx(char_fx: CharFXTransform) -> bool: + var waitTime: float = char_fx.env.get(\"time\", 1.0) + var speed: float = char_fx.env.get(\"speed\", 16.0) + + if char_fx.elapsed_time > waitTime: + char_fx.visible = false + if float(char_fx.elapsed_time) > ( (float(char_fx.relative_index) / speed) + float(waitTime) ): + char_fx.visible = true + else: + char_fx.visible = false + return true +" + +[sub_resource type="RichTextEffect" id="RichTextEffect_jiwwu"] +script = SubResource("GDScript_qmumq") + +[node name="Dialogue" type="Control"] +layout_mode = 3 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -384.0 +offset_top = 200.0 +offset_right = -384.0 +offset_bottom = 200.0 +grow_horizontal = 2 +grow_vertical = 2 +scale = Vector2(2, 2) +script = ExtResource("1") + +[node name="DialogueContainer" type="HBoxContainer" parent="."] +layout_mode = 0 +offset_right = 392.0 +offset_bottom = 72.0 +size_flags_vertical = 8 +theme_override_constants/separation = 0 + +[node name="PortraitBox" type="MarginContainer" parent="DialogueContainer"] +custom_minimum_size = Vector2(72, 72) +layout_mode = 2 +script = ExtResource("9") + +[node name="Panel" type="Panel" parent="DialogueContainer/PortraitBox"] +layout_mode = 2 +theme = ExtResource("3") + +[node name="Portrait" type="AnimatedSprite2D" parent="DialogueContainer/PortraitBox/Panel"] +position = Vector2(4, 4) +sprite_frames = ExtResource("9_rdh7m") +animation = &"idle" +centered = false +script = ExtResource("11") + +[node name="TextBox" type="MarginContainer" parent="DialogueContainer"] +custom_minimum_size = Vector2(320, 70) +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("8") + +[node name="Panel" type="Panel" parent="DialogueContainer/TextBox"] +layout_mode = 2 +theme = ExtResource("3") + +[node name="Text" type="RichTextLabel" parent="DialogueContainer/TextBox/Panel"] +auto_translate_mode = 2 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 6.0 +offset_top = 3.5 +offset_right = -6.0 +offset_bottom = -3.5 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("3") +bbcode_enabled = true +scroll_active = false +custom_effects = [SubResource("RichTextEffect_omcs0"), SubResource("RichTextEffect_mfy4b"), SubResource("RichTextEffect_p8ibk"), SubResource("RichTextEffect_i3ro0"), SubResource("RichTextEffect_kkw1g"), SubResource("RichTextEffect_s5tcp"), SubResource("RichTextEffect_xw4hv"), SubResource("RichTextEffect_jiwwu")] +visible_characters_behavior = 2 + +[node name="FinishEffect" type="Sprite2D" parent="DialogueContainer/TextBox"] +position = Vector2(311, 63.5) +rotation = 1.5708 +scale = Vector2(0.25, 0.25) +texture = ExtResource("5") +script = ExtResource("10_typqo") + +[node name="NameBox" type="MarginContainer" parent="."] +custom_minimum_size = Vector2(64, 25) +layout_mode = 0 +offset_left = 74.0 +offset_top = -24.0 +offset_right = 138.0 +offset_bottom = 1.0 + +[node name="Panel" type="Panel" parent="NameBox"] +layout_mode = 2 +theme = ExtResource("2") + +[node name="TextBox" type="MarginContainer" parent="NameBox"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 + +[node name="Text" type="RichTextLabel" parent="NameBox/TextBox"] +layout_mode = 2 +theme = ExtResource("2") +bbcode_enabled = true +fit_content = true +scroll_active = false +autowrap_mode = 0 +visible_characters_behavior = 2 +script = ExtResource("12_mja5r") + +[node name="AudioPlayer" type="AudioStreamPlayer2D" parent="."] +script = ExtResource("10") diff --git a/Scenes/DialogueTheme.tscn b/Scenes/DialogueTheme.tscn new file mode 100755 index 0000000..fefa2a0 --- /dev/null +++ b/Scenes/DialogueTheme.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/Suada/Assets/Themes/main_theme.tres" type="Theme" id=2] + +[node name="Node2D" type="Node2D"] + +[node name="Frame" type="Node2D" parent="."] + +[node name="Panel" type="Panel" parent="Frame"] +margin_right = 224.0 +margin_bottom = 64.0 +theme = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Text" type="Node2D" parent="."] +position = Vector2( 6, 4 ) +scale = Vector2( 0.4, 0.4 ) + +[node name="RichTextLabel" type="RichTextLabel" parent="Text"] +margin_left = -5.0 +margin_right = 535.0 +margin_bottom = 140.0 +rect_clip_content = false +theme = ExtResource( 2 ) +bbcode_enabled = true +scroll_active = false +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/SuadaPlugin.gd b/SuadaPlugin.gd index 28586e1..866fe6e 100644 --- a/SuadaPlugin.gd +++ b/SuadaPlugin.gd @@ -1,13 +1,10 @@ @tool - extends EditorPlugin -const AUTOLOAD_NAME = "SuadaGlobals" - func _enter_tree(): - add_autoload_singleton(AUTOLOAD_NAME, "res://addons/Suada/Nodes/SuadaGlobals.gd") + add_autoload_singleton("SuadaConfig", "res://addons/Suada/Nodes/Globals/SuadaConfig.gd") func _exit_tree(): - remove_autoload_singleton(AUTOLOAD_NAME) + remove_autoload_singleton("SuadaConfig") diff --git a/SuadaPlugin.gd.uid b/SuadaPlugin.gd.uid new file mode 100644 index 0000000..fc08e94 --- /dev/null +++ b/SuadaPlugin.gd.uid @@ -0,0 +1 @@ +uid://c4j5hp6dyqpbg diff --git a/plugin.cfg b/plugin.cfg index eff3387..e2dccef 100644 --- a/plugin.cfg +++ b/plugin.cfg @@ -1,7 +1,7 @@ [plugin] name="Suada" -description="Dialogue system plugin for Godot 3" +description="Dialogue system plugin for Godot 4" author="Ganger Games" -version="0.1.0" +version="0.2.0" script="SuadaPlugin.gd" diff --git a/requirements.txt b/requirements.txt index 03995a7..13d7575 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -gdtoolkit==4.1.0 +gdtoolkit==4.3.3