class_name VNTextbox extends PanelContainer const VN_REVEAL_TIME = 0.01 const VISIBLE_LINES:int = 4 var label:RichTextLabel; var text:String = "" 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; func _ready() -> void: label = $MarginContainer/Label self.visible = false; func _process(delta: float) -> void: if text == "": return; if currentViewScrolled: if Input.is_action_just_pressed("interact"): if isMoreViews: visibleCharacters = 0; currentLine += VISIBLE_LINES; currentViewScrolled = false; recalculateWrapping(); return isClosed = true; setText(""); return; if visibleCharacters >= getCountOfCharactersToScrollInView(): currentViewScrolled = true; #print("Scrolled view"); #if isMoreViews: #print("More views"); return; if Input.is_action_just_pressed("interact"): isSpeedupDown = true; elif Input.is_action_just_released("interact"): isSpeedupDown = false; elif !Input.is_action_pressed("interact"): isSpeedupDown = false; revealTimer += delta; if isSpeedupDown: revealTimer += delta; 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.text = 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.text = wrappedText; label.fit_content = false; label.visible_characters = 0; if lineStarts.size() > currentLine + VISIBLE_LINES: isMoreViews = true; func setText(text:String) -> void: self.text = text; if text == "": isClosed = true; self.visible = false; return; isClosed = false; revealTimer = 0; visibleCharacters = 0; currentLine = 0; currentViewScrolled = false; recalculateWrapping(); self.visible = true;