diff --git a/entity/npc/NPC.gd b/entity/npc/NPC.gd index 02e2ab7..b94db2d 100644 --- a/entity/npc/NPC.gd +++ b/entity/npc/NPC.gd @@ -31,7 +31,7 @@ class_name NPC extends CharacterBody3D return 64.0 func onInteract(player:Player) -> void: - print("Interacted with NPC") + UI.TEXTBOX.setText("Hello, I'm an NPC!\nThis is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10"); pass func _enter_tree() -> void: diff --git a/map/TestMap.gd b/map/TestMap.gd index bf4da3d..67e954f 100644 --- a/map/TestMap.gd +++ b/map/TestMap.gd @@ -1,12 +1 @@ extends Node3D - -func _ready(): - UI.MADTALK.start_dialog("bare_minimum") - -func _input(event): - if ( - (event is InputEventKey) and (event.pressed) and (not event.echo) and (event.keycode in [KEY_SPACE, KEY_ENTER, KEY_KP_ENTER]) - ) or ( - (event is InputEventMouseButton) and (event.pressed) and (event.button_index == MOUSE_BUTTON_LEFT) - ): - UI.MADTALK.dialog_acknowledge() diff --git a/map/TestMap.tscn b/map/TestMap.tscn index 7243ed9..bd600f3 100644 --- a/map/TestMap.tscn +++ b/map/TestMap.tscn @@ -11,7 +11,7 @@ script = ExtResource("1_6ms5s") [node name="TestMapBase" parent="." instance=ExtResource("1_ox0si")] [node name="Player" parent="." instance=ExtResource("2_0d2qr")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 27.142, 1.94879, -59.112) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.04397, 1.9488, -16.5251) facingDirection = 1 [node name="NPC" parent="." instance=ExtResource("3_0vfw4")] diff --git a/singleton/Pause.gd b/singleton/Pause.gd index 9ea2340..ec418db 100644 --- a/singleton/Pause.gd +++ b/singleton/Pause.gd @@ -27,4 +27,4 @@ func menuPause() -> void: if UI.PAUSE.isOpen(): UI.PAUSE.close() else: - UI.PAUSE.open() \ No newline at end of file + UI.PAUSE.open() diff --git a/ui/component/VNTextbox.gd b/ui/component/VNTextbox.gd index ce04ab5..3483843 100644 --- a/ui/component/VNTextbox.gd +++ b/ui/component/VNTextbox.gd @@ -1,51 +1,49 @@ class_name VNTextbox extends PanelContainer const VN_REVEAL_TIME = 0.01 -const VISIBLE_LINES:int = 4 -var label:RichTextLabel; -var text:String = "" +var label:AdvancedRichText; var parsedOutText = "" -var visibleCharacters:int = 0; var revealTimer:float = 0; var lineStarts:Array[int] = []; var newlineIndexes:Array[int] = []; -var wrappedText:String = ""; -var currentLine = 0; var currentViewScrolled = true; var isSpeedupDown = false; -var isMoreViews = false; -var isClosed = true; + +var isClosed:bool = false: + get(): + return !self.visible; + set(value): + self.visible = !value; signal textboxClosing func _ready() -> void: label = $MarginContainer/Label - self.visible = false; + isClosed = true func _process(delta: float) -> void: - if text == "": + if label.getFinalText() == "": isClosed = true; return; - if currentViewScrolled: - if Input.is_action_just_pressed("interact"): - if isMoreViews: - visibleCharacters = 0; - currentLine += VISIBLE_LINES; + if label.visible_characters >= label.getCharactersDisplayedCount(): + if (label.maxLines + label.startLine) < label.getTotalLineCount(): + # Not on last page. + if Input.is_action_just_pressed("interact"): + label.startLine += label.maxLines; + label.visible_characters = 0; currentViewScrolled = false; - recalculateWrapping(); return - setText(""); - textboxClosing.emit(); - return; - if visibleCharacters >= getCountOfCharactersToScrollInView(): - currentViewScrolled = true; - #print("Scrolled view"); - #if isMoreViews: - #print("More views"); + currentViewScrolled = true; + else: + # On last page + if Input.is_action_just_released("interact"): + textboxClosing.emit(); + isClosed = true; + currentViewScrolled = true return; if Input.is_action_just_pressed("interact"): @@ -61,63 +59,15 @@ func _process(delta: float) -> void: if revealTimer > VN_REVEAL_TIME: revealTimer = 0; - visibleCharacters += 1; - label.visible_characters = visibleCharacters; - -func getCountOfCharactersToScrollInView() -> int: - if lineStarts.size() <= VISIBLE_LINES: - return wrappedText.length(); - if currentLine + VISIBLE_LINES >= lineStarts.size(): - return wrappedText.length() - lineStarts[currentLine]; - return lineStarts[min(lineStarts.size(), currentLine + VISIBLE_LINES)] - lineStarts[currentLine]; - -func recalculateWrapping(): - # Reset label to default. - label.advancedText = text; - label.visible_characters = -1; - label.fit_content = true; - isMoreViews = false; - - # Determine where the wrapped newlines are - lineStarts = [ 0 ]; - var line = 0; - var wasNewLine = false; - for i in range(0, text.length()): - var tLine = label.get_character_line(i); - if tLine == line: - wasNewLine = false - if text[i] == "\n": - wasNewLine = true - continue; - if !wasNewLine: - newlineIndexes.append(i); - lineStarts.append(i); - line = tLine; - - # Create fake pre-wrapped text. - wrappedText = ""; - for i in range(lineStarts[currentLine], text.length()): - if newlineIndexes.find(i) != -1 and i != lineStarts[currentLine]: - wrappedText += "\n"; - wrappedText += text[i]; - - label.advancedText = wrappedText; - label.fit_content = false; - label.visible_characters = 0; - - if lineStarts.size() > currentLine + VISIBLE_LINES: - isMoreViews = true; + label.visible_characters += 1; func setText(text:String) -> void: - self.text = text; - if text == "": - self.visible = false; - return; - isClosed = false; revealTimer = 0; - visibleCharacters = 0; - currentLine = 0; currentViewScrolled = false; - recalculateWrapping(); - self.visible = true; + label.visible_characters = 0; + label.startLine = 0; + label.text = "" + await get_tree().process_frame# Absolutely needed to make the text wrap + label.text = text; + label.visible_characters = 0; diff --git a/ui/component/VNTextbox.tscn b/ui/component/VNTextbox.tscn index 6a010c8..7974e71 100644 --- a/ui/component/VNTextbox.tscn +++ b/ui/component/VNTextbox.tscn @@ -1,11 +1,11 @@ -[gd_scene load_steps=5 format=3 uid="uid://bkx3l0kckf4a8"] +[gd_scene load_steps=4 format=3 uid="uid://bkx3l0kckf4a8"] [ext_resource type="Theme" uid="uid://dm7ee4aqjr2dl" path="res://ui/UI Theme.tres" id="1_wx4lp"] [ext_resource type="Script" uid="uid://h8lw23ypcfty" path="res://ui/component/VNTextbox.gd" id="2_uo1gm"] -[ext_resource type="Script" uid="uid://c6av3xe4m2ujl" path="res://addons/madtalk/runtime/madtalk_runtime.gd" id="3_2x6s1"] [ext_resource type="Script" uid="uid://bjj6upgk1uvxd" path="res://ui/component/advancedrichtext/AdvancedRichText.gd" id="3_m60k3"] [node name="VNTextbox" type="PanelContainer"] +clip_contents = true anchors_preset = 12 anchor_top = 1.0 anchor_right = 1.0 @@ -16,13 +16,8 @@ grow_vertical = 0 theme = ExtResource("1_wx4lp") script = ExtResource("2_uo1gm") -[node name="MadTalk" type="Node" parent="."] -script = ExtResource("3_2x6s1") -DialogMainControl = NodePath("..") -DialogMessageBox = NodePath("../MarginContainer") -DialogMessageLabel = NodePath("../MarginContainer/Label") - [node name="MarginContainer" type="MarginContainer" parent="."] +clip_contents = true layout_mode = 2 theme = ExtResource("1_wx4lp") theme_override_constants/margin_left = 4 @@ -34,8 +29,43 @@ theme_override_constants/margin_bottom = 4 layout_mode = 2 theme = ExtResource("1_wx4lp") bbcode_enabled = true -text = "To get through tight spaces, press[input action=down][/input]" -fit_content = true +text = "Hello, I'm an NPC! +This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10" script = ExtResource("3_m60k3") -userText = "To get through tight spaces, press[input action=down][/input]" -finalText = "To get through tight spaces, press[img height=12 valign=center,center]res://ui/input/down.tres[/img]" +userText = "Hello, I'm an NPC! +This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10" +_finalText = "Hello, I'm an NPC! +This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10" +_newLineIndexes = Array[int]([0]) +_lines = PackedStringArray("Hello, I\'m an NPC!", "This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot\'s built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing", "Line 1", "Line 2", "Line 3", "Line 4", "Line 5", "Line 6", "Line 7", "Line 8", "Line 9", "Line 10") +maxLines = 4 diff --git a/ui/component/advancedrichtext/AdvancedRichText.gd b/ui/component/advancedrichtext/AdvancedRichText.gd index bffee69..f43e49c 100644 --- a/ui/component/advancedrichtext/AdvancedRichText.gd +++ b/ui/component/advancedrichtext/AdvancedRichText.gd @@ -2,7 +2,10 @@ class_name AdvancedRichText extends RichTextLabel @export_multiline var userText:String = "" # The text the user is asking for -@export_multiline var finalText:String = "" # The final text after processing (translation, wrapping, etc.) +@export_multiline var _finalText:String = "" # The final text after processing (translation, wrapping, etc.) + +@export var _newLineIndexes:Array[int] = [] # The indexes of where each line starts in finalText +@export var _lines:PackedStringArray = []; # Hides the original RichTextLabel text property func _set(property: StringName, value) -> bool: @@ -36,17 +39,43 @@ func _get(property: StringName): get(): return smartWrap -func _init() -> void: - _recalcText() +@export var maxLines:int = -1: + set(value): + maxLines = value + _recalcText() + get(): + return maxLines + +@export var startLine:int = 0: + set(value): + startLine = value + _recalcText() + get(): + return startLine + +# Returns count of characters that can be displayed, assuming visible_chars = -1 +func getCharactersDisplayedCount() -> int: + # Count characters + var count = 0 + var lineCount = min(startLine + maxLines, _lines.size()) - startLine + for i in range(startLine, startLine + lineCount): + count += _lines[i].length() + if lineCount > 1: + count += lineCount - 1 # Add newlines + return count + +func getFinalText() -> String: + return _finalText + +func getTotalLineCount() -> int: + return _lines.size() func _enter_tree() -> void: - _recalcText() - self.resized.connect(_recalcText) - -func _exit_tree() -> void: - self.resized.disconnect(_recalcText) + self.threaded = false; func _recalcText() -> void: + _lines.clear() + if userText.is_empty(): self.richtextlabel_text = "" return @@ -63,8 +92,6 @@ func _recalcText() -> void: for match in regex.search_all(textTranslated): var action = match.get_string(1).to_lower() var height:int = get_theme_font_size("normal_font_size") - # var device = get_current_device_type() - # var icon_path = get_icon_for_action(action, device) var img_tag = "[img height=%d valign=center,center]res://ui/input/%s.tres[/img]" % [ height, action ] inputIconText = inputIconText.replace(match.get_string(0), img_tag) @@ -76,29 +103,39 @@ func _recalcText() -> void: self.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART; self.richtextlabel_text = unwrappedText self.visible_characters = -1; - self.fit_content = true; - var newlineIndexes = []; + self.fit_content = false; + _newLineIndexes = []; # Determine where the wrapped newlines are var line = 0; var wasNewLine = false; - for i in range(0, unwrappedText.length()): + for i in range(0, self.richtextlabel_text.length()): var tLine = self.get_character_line(i); - if tLine == line || tLine == -1: + if tLine == line: wasNewLine = false - if unwrappedText[i] == "\n": + if self.richtextlabel_text[i] == "\n": wasNewLine = true continue; if !wasNewLine: - newlineIndexes.append(i); + _newLineIndexes.append(i); line = tLine; # Create fake pre-wrapped text. wrappedText = ""; - for i in range(0, unwrappedText.length()): - if newlineIndexes.find(i) != -1 and i != 0: + for i in range(0, self.richtextlabel_text.length()): + if _newLineIndexes.find(i) != -1 and i != 0: wrappedText += "\n"; - wrappedText += unwrappedText[i]; + wrappedText += self.richtextlabel_text[i]; - finalText = wrappedText - self.richtextlabel_text = wrappedText + # Handle max and start line(s) + var maxText = wrappedText + if maxLines > 0: + _lines = maxText.split("\n", true); + var selectedLines = []; + for i in range(startLine, min(startLine + maxLines, _lines.size())): + selectedLines.append(_lines[i]); + maxText = "\n".join(selectedLines); + + _finalText = maxText + self.richtextlabel_text = maxText + print("Updated text")