179 lines
5.4 KiB
GDScript
179 lines
5.4 KiB
GDScript
extends PanelContainer
|
||
## Sohbet paneli — kod ile kurulur (tscn'de yok), WsClient chat sinyallerini dinler.
|
||
## hud.gd oluşturup HUD CanvasLayer'ına ekler. Node adı "ChatPanel" (world.gd blokaj kontrolü).
|
||
|
||
const MAX_ROWS := 60
|
||
const PANEL_HEIGHT := 480.0
|
||
|
||
var _list: VBoxContainer
|
||
var _scroll: ScrollContainer
|
||
var _input: LineEdit
|
||
var _tab_global: Button
|
||
var _tab_local: Button
|
||
|
||
## Aktif kanal: "global" | "local". Mesajlar kanal başına bellekte tutulur,
|
||
## sekme değişince yeniden çizilir.
|
||
var _channel := "global"
|
||
var _msgs := {"global": [], "local": []}
|
||
|
||
func _ready() -> void:
|
||
name = "ChatPanel"
|
||
visible = false
|
||
_build_ui()
|
||
WsClient.chat_received.connect(_on_chat)
|
||
WsClient.chat_history.connect(_on_history)
|
||
WsClient.local_chat_history.connect(_on_local_history)
|
||
# Lokasyon değişince yerel geçmiş bayatlar
|
||
GameState.traveled.connect(func(_p: Dictionary) -> void:
|
||
_msgs["local"] = []
|
||
if visible and _channel == "local":
|
||
WsClient.request_local_history())
|
||
|
||
func _build_ui() -> void:
|
||
set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
|
||
offset_top = -PANEL_HEIGHT
|
||
offset_left = 8
|
||
offset_right = -8
|
||
offset_bottom = -8
|
||
var sb := StyleBoxFlat.new()
|
||
sb.bg_color = Color(0.08, 0.1, 0.13, 0.95)
|
||
sb.set_corner_radius_all(10)
|
||
add_theme_stylebox_override("panel", sb)
|
||
|
||
var margin := MarginContainer.new()
|
||
margin.add_theme_constant_override("margin_left", 12)
|
||
margin.add_theme_constant_override("margin_right", 12)
|
||
margin.add_theme_constant_override("margin_top", 8)
|
||
margin.add_theme_constant_override("margin_bottom", 8)
|
||
add_child(margin)
|
||
|
||
var vbox := VBoxContainer.new()
|
||
vbox.add_theme_constant_override("separation", 6)
|
||
margin.add_child(vbox)
|
||
|
||
var top := HBoxContainer.new()
|
||
var title := Label.new()
|
||
title.text = "💬 Sohbet"
|
||
title.add_theme_font_size_override("font_size", 18)
|
||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
top.add_child(title)
|
||
_tab_global = Button.new()
|
||
_tab_global.text = "🌍 Genel"
|
||
_tab_global.toggle_mode = true
|
||
_tab_global.button_pressed = true
|
||
_tab_global.pressed.connect(func() -> void: _switch_channel("global"))
|
||
top.add_child(_tab_global)
|
||
_tab_local = Button.new()
|
||
_tab_local.text = "📍 Burada"
|
||
_tab_local.toggle_mode = true
|
||
_tab_local.pressed.connect(func() -> void: _switch_channel("local"))
|
||
top.add_child(_tab_local)
|
||
var close_btn := Button.new()
|
||
close_btn.text = "✕"
|
||
close_btn.pressed.connect(func() -> void: visible = false)
|
||
top.add_child(close_btn)
|
||
vbox.add_child(top)
|
||
|
||
_scroll = ScrollContainer.new()
|
||
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||
vbox.add_child(_scroll)
|
||
|
||
_list = VBoxContainer.new()
|
||
_list.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_list.add_theme_constant_override("separation", 4)
|
||
_scroll.add_child(_list)
|
||
|
||
var bottom := HBoxContainer.new()
|
||
bottom.add_theme_constant_override("separation", 8)
|
||
_input = LineEdit.new()
|
||
_input.placeholder_text = "Mesaj yaz…"
|
||
_input.max_length = 300
|
||
_input.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_input.text_submitted.connect(func(_t: String) -> void: _send())
|
||
bottom.add_child(_input)
|
||
var send_btn := Button.new()
|
||
send_btn.text = "Gönder"
|
||
send_btn.pressed.connect(_send)
|
||
bottom.add_child(send_btn)
|
||
vbox.add_child(bottom)
|
||
|
||
func open() -> void:
|
||
visible = true
|
||
if _channel == "local" and (_msgs["local"] as Array).is_empty():
|
||
WsClient.request_local_history()
|
||
_scroll_to_bottom()
|
||
_input.grab_focus()
|
||
|
||
func _switch_channel(ch: String) -> void:
|
||
_channel = ch
|
||
_tab_global.button_pressed = ch == "global"
|
||
_tab_local.button_pressed = ch == "local"
|
||
_input.placeholder_text = "Mesaj yaz…" if ch == "global" else "Buradakilere yaz…"
|
||
if ch == "local":
|
||
WsClient.request_local_history()
|
||
_rerender()
|
||
|
||
func _send() -> void:
|
||
var msg := _input.text.strip_edges()
|
||
if msg == "":
|
||
return
|
||
WsClient.send_chat(msg, _channel)
|
||
_input.text = ""
|
||
_input.grab_focus()
|
||
|
||
func _on_history(messages: Array) -> void:
|
||
_msgs["global"] = messages.duplicate()
|
||
if _channel == "global":
|
||
_rerender()
|
||
|
||
func _on_local_history(messages: Array) -> void:
|
||
_msgs["local"] = messages.duplicate()
|
||
if _channel == "local":
|
||
_rerender()
|
||
|
||
func _on_chat(msg: Dictionary) -> void:
|
||
var ch := str(msg.get("channel", "global"))
|
||
if not _msgs.has(ch):
|
||
return
|
||
var arr: Array = _msgs[ch]
|
||
arr.append(msg)
|
||
while arr.size() > MAX_ROWS:
|
||
arr.pop_front()
|
||
if ch == _channel:
|
||
_append_row(msg)
|
||
while _list.get_child_count() > MAX_ROWS:
|
||
var oldest := _list.get_child(0)
|
||
_list.remove_child(oldest)
|
||
oldest.queue_free()
|
||
_scroll_to_bottom()
|
||
|
||
func _rerender() -> void:
|
||
for c in _list.get_children():
|
||
c.queue_free()
|
||
for m in (_msgs[_channel] as Array):
|
||
if m is Dictionary:
|
||
_append_row(m)
|
||
_scroll_to_bottom()
|
||
|
||
func _append_row(msg: Dictionary) -> void:
|
||
var own: bool = int(msg.get("char_id", 0)) == int(GameState.character.get("id", -1))
|
||
var row := RichTextLabel.new()
|
||
row.bbcode_enabled = true
|
||
row.fit_content = true
|
||
row.scroll_active = false
|
||
row.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
var name_color := "#7ee08a" if own else "#e0c060"
|
||
row.text = "[color=%s]%s:[/color] %s" % [
|
||
name_color,
|
||
str(msg.get("name", "?")).replace("[", "["),
|
||
str(msg.get("text", "")).replace("[", "["),
|
||
]
|
||
_list.add_child(row)
|
||
|
||
func _scroll_to_bottom() -> void:
|
||
if not visible:
|
||
return
|
||
await get_tree().process_frame
|
||
_scroll.scroll_vertical = int(_scroll.get_v_scroll_bar().max_value)
|