From feba26839bda10d516ca6e9040c278d83b321f1e Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Mon, 2 Mar 2026 02:07:05 -0600 Subject: [PATCH] Created ChatBox UI Created ChatBox UI for display chat in the Overlay. --- UI/Controls/chat_box.gd | 139 ++++++++++++++++++++++++++++++++++++ UI/Controls/chat_box.gd.uid | 1 + UI/Controls/chat_box.tscn | 26 +++++++ 3 files changed, 166 insertions(+) create mode 100644 UI/Controls/chat_box.gd create mode 100644 UI/Controls/chat_box.gd.uid create mode 100644 UI/Controls/chat_box.tscn diff --git a/UI/Controls/chat_box.gd b/UI/Controls/chat_box.gd new file mode 100644 index 00000000..2e5403ce --- /dev/null +++ b/UI/Controls/chat_box.gd @@ -0,0 +1,139 @@ +@tool +extends Control +class_name ChatBox + +enum ChatDock { BOTTOM, LEFT, TOP, RIGHT } +@onready var chat_history: ScrollContainer = %ChatHistory +@onready var history: VBoxContainer = %History +@onready var height: int = 250 +@onready var width: int = 200 + +@export var dock: ChatDock = ChatDock.BOTTOM: + set(value): + dock = value + if not is_node_ready(): return + _orientate_chatbox() + +func _ready() -> void: + var waiting_for_ready := false + if Engine.is_editor_hint(): + return + while Globals.twitcher == null or !Globals.twitcher.is_node_ready(): + if not waiting_for_ready: + print("Waiting for Twitcher Ready...") + waiting_for_ready = true + await get_tree().process_frame + print("Twitcher ready!") + _orientate_chatbox() + + Globals.twitcher.chat.message_received.connect(_handle_chat) + +func _orientate_chatbox() -> void: + match dock: + ChatDock.BOTTOM: + chat_history.custom_minimum_size = Vector2(1550,265) + chat_history.size = Vector2(1550,265) + chat_history.position = Vector2(0, get_viewport_rect().size.y - 265) + #chat_history.set_anchors_preset(Control.PRESET_BOTTOM_WIDE) + #chat_history.custom_minimum_size = Vector2(0,height) + ChatDock.LEFT: + chat_history.custom_minimum_size = Vector2(500,get_viewport_rect().size.y) + chat_history.size = Vector2(500,get_viewport_rect().size.y) + chat_history.position = Vector2.ZERO + #chat_history.set_anchors_preset(Control.PRESET_LEFT_WIDE) + #chat_history.custom_minimum_size = Vector2(width,0) + ChatDock.RIGHT: + chat_history.custom_minimum_size = Vector2(500, get_viewport_rect().size.y - 265) + chat_history.size = Vector2(500, 265) + chat_history.position = Vector2(get_viewport_rect().size.x - 500, 0) + #chat_history.set_anchors_preset(Control.PRESET_RIGHT_WIDE) + #chat_history.custom_minimum_size = Vector2(width,0) + ChatDock.TOP: + chat_history.custom_minimum_size = Vector2(1550,265) + chat_history.size = Vector2(1550,265) + chat_history.position = Vector2.ZERO + #chat_history.set_anchors_preset(Control.PRESET_TOP_WIDE) + #chat_history.custom_minimum_size = Vector2(0,height) + +func _handle_chat(message: TwitchChatMessage) -> void: + var badges_dict: Dictionary = await message.get_badges(Globals.twitcher.media) + var badges: Array[SpriteFrames] = [] + badges.assign(badges_dict.values()) + + var result_message: String = "" + var badge_id: int = 0 + for badge: SpriteFrames in badges: + result_message += "[sprite id='b-%s']%s[/sprite]" % [badge_id, badge.resource_path] + badge_id += 1 + result_message += "[color=%s]%s[/color]" % [message.get_color(), message.chatter_user_name] + + match message.message_type: + TwitchChatMessage.MessageType.text: + result_message = await show_text(message, result_message) + + TwitchChatMessage.MessageType.power_ups_gigantified_emote: + result_message = await show_text(message, result_message, 3) + + TwitchChatMessage.MessageType.channel_points_highlighted: + result_message += "[bgcolor=#755ebc][color=#e9fffb]" + result_message = await show_text(message, result_message) + result_message += "[/color][/bgcolor]" + + TwitchChatMessage.MessageType.power_ups_message_effect: + result_message += "[shake rate=20.0 level=5 connected=1]" + result_message = await show_text(message, result_message) + result_message += "[/shake]" + + append_message(result_message) + +func show_text(message: TwitchChatMessage, current_text: String, emote_scale: int = 1) -> String: + await message.load_emotes_from_fragment(Globals.twitcher.media) + var frag_id: int = 0 + for fragment: TwitchChatMessage.Fragment in message.message.fragments: + frag_id += 1 + match fragment.type: + TwitchChatMessage.FragmentType.text: + current_text += fragment.text + TwitchChatMessage.FragmentType.cheermote: + var cheermote_scale: StringName = TwitchCheermoteDefinition.SCALE_MAP.get(emote_scale, TwitchCheermoteDefinition.SCALE_1) + var cheermote: SpriteFrames = await fragment.cheermote.get_sprite_frames(Globals.twitcher.media, cheermote_scale) + current_text += "[sprite id='f-%s']%s[/sprite]" % [frag_id, cheermote.resource_path] + TwitchChatMessage.FragmentType.emote: + var emote: SpriteFrames = await fragment.emote.get_sprite_frames(Globals.twitcher.media, emote_scale) + current_text += "[sprite id='f-%s']%s[/sprite]" % [frag_id, emote.resource_path] + TwitchChatMessage.FragmentType.mention: + current_text += "[color=%s]@%s[/color]" % ["%00a0b6", fragment.mention.user_name] + return current_text + +func append_message(msg: String) -> void: + var cm: RichTextLabel = RichTextLabel.new() + cm.bbcode_enabled = true + cm.fit_content = true + + var se: SpriteFrameEffect = SpriteFrameEffect.new() + cm.install_effect(se) + %History.add_child(cm) + + msg = se.prepare_message(msg, cm) + cm.text = _get_time() + " " + msg + + _clean_old_messages() + + await get_tree().process_frame + var rect = cm.get_rect() + %ChatHistory.set_deferred("scroll_vertical",rect.position.y + rect.size.y) + +func _clean_old_messages() -> void: + var child_count := %ChatHistory.get_child_count() + if child_count < 1000: return + for i in child_count - 1000: + %ChatHistory.get_child(i).queue_free() + +func _get_time() -> String: + var time: Dictionary = Time.get_time_dict_from_system() + var pm := false + if time["hour"] >= 12: + pm = true + if time["hour"] > 12: + time["hour"] -= 12 + return "%02d:%02d%s" % [time["hour"], time["minute"], "PM" if pm else "AM"] diff --git a/UI/Controls/chat_box.gd.uid b/UI/Controls/chat_box.gd.uid new file mode 100644 index 00000000..fad5ef38 --- /dev/null +++ b/UI/Controls/chat_box.gd.uid @@ -0,0 +1 @@ +uid://dcikt7pf001m8 diff --git a/UI/Controls/chat_box.tscn b/UI/Controls/chat_box.tscn new file mode 100644 index 00000000..afee45a0 --- /dev/null +++ b/UI/Controls/chat_box.tscn @@ -0,0 +1,26 @@ +[gd_scene format=3 uid="uid://cstq30mjx0pch"] + +[ext_resource type="Script" uid="uid://dcikt7pf001m8" path="res://UI/Controls/chat_box.gd" id="1_dg5g0"] + +[node name="ChatBox" type="Control" unique_id=859214320] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_dg5g0") + +[node name="ChatHistory" type="ScrollContainer" parent="." unique_id=1482465110] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 300) +layout_mode = 0 +offset_bottom = 300.0 +follow_focus = true +horizontal_scroll_mode = 0 +vertical_scroll_mode = 3 + +[node name="History" type="VBoxContainer" parent="ChatHistory" unique_id=345296200] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3