Build first lobby browser UI
This commit is contained in:
@@ -14,6 +14,7 @@ Der aktuelle Stand ist bewusst ein technischer Prototyp. Es ist noch keine ferti
|
|||||||
- Brotatos Run-Szene wird per Script-Extension beobachtet.
|
- Brotatos Run-Szene wird per Script-Extension beobachtet.
|
||||||
- Run-Start, Spieler-Spawn und einfache Player-Snapshots werden geloggt bzw. fuer P2P-Versand vorbereitet.
|
- Run-Start, Spieler-Spawn und einfache Player-Snapshots werden geloggt bzw. fuer P2P-Versand vorbereitet.
|
||||||
- Das Brogether-Modal ist standardmaessig versteckt und kann ueber das Hauptmenue oder `F5` geoeffnet werden.
|
- Das Brogether-Modal ist standardmaessig versteckt und kann ueber das Hauptmenue oder `F5` geoeffnet werden.
|
||||||
|
- Die UI enthaelt einen ersten Lobby-Browser, Lobby-Erstellung mit optionalem Passwort und einen Wartebereich mit Spielerkacheln.
|
||||||
|
|
||||||
## Was noch nicht fertig ist
|
## Was noch nicht fertig ist
|
||||||
|
|
||||||
@@ -54,6 +55,11 @@ Danach Brotato komplett neu starten.
|
|||||||
|
|
||||||
Brogether ist im Spiel standardmaessig unsichtbar. Im Hauptmenue gibt es einen neuen `BROGETHER`-Button; alternativ oeffnet `F5` das Brogether-Modal. Das Modal pausiert das Spiel wie Brotatos eigenes F8-Feedback-Fenster und kann mit `Escape`, `F5` oder dem Close-Button wieder geschlossen werden.
|
Brogether ist im Spiel standardmaessig unsichtbar. Im Hauptmenue gibt es einen neuen `BROGETHER`-Button; alternativ oeffnet `F5` das Brogether-Modal. Das Modal pausiert das Spiel wie Brotatos eigenes F8-Feedback-Fenster und kann mit `Escape`, `F5` oder dem Close-Button wieder geschlossen werden.
|
||||||
|
|
||||||
|
Im Modal gibt es jetzt zwei Bereiche:
|
||||||
|
|
||||||
|
- Lobby-Browser: zeigt gefundene Brogether-Lobbys mit Name, Host, Spielerzahl und Passwortstatus.
|
||||||
|
- Wartebereich: zeigt die Spieler in der aktuellen Lobby als Kacheln mit Steam-Namen und statischem Initialen-Avatar.
|
||||||
|
|
||||||
## Debug-Tasten
|
## Debug-Tasten
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -65,7 +71,7 @@ F10 Ping senden
|
|||||||
F11 Lokalen Loopback-Test starten
|
F11 Lokalen Loopback-Test starten
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Brogether-Modal enthaelt Buttons fuer Host, Join, Leave, Ping und Loopback. Es ist fuer die naechsten Zwei-Client-Tests gedacht und ersetzt die meisten Hotkey-Aktionen.
|
Das Brogether-Modal enthaelt Buttons fuer Host, Join, Leave und Ping. Es ist fuer die naechsten Zwei-Client-Tests gedacht und ersetzt die meisten Hotkey-Aktionen. Passwort-Lobbys sind aktuell eine Mod-seitige Zugangskontrolle: die Lobby bleibt sichtbar, der Join-Button prueft aber das Passwort, bevor Brogether beitritt.
|
||||||
|
|
||||||
Brogether schreibt ein eigenes Log nach:
|
Brogether schreibt ein eigenes Log nach:
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ Danach Brotato ueber Steam starten. Die Mod sollte im Mod-Menue auftauchen.
|
|||||||
|
|
||||||
Brogether selbst bleibt danach unsichtbar, bis es geoeffnet wird. Im Hauptmenue fuegt die Mod einen `BROGETHER`-Button hinzu. Alternativ oeffnet `F5` dasselbe Modal. Das Modal verhaelt sich bewusst wie Brotatos F8-Feedback-Fenster: es pausiert, legt sich ueber das Spiel und kann mit `Escape`, `F5` oder `Close` geschlossen werden.
|
Brogether selbst bleibt danach unsichtbar, bis es geoeffnet wird. Im Hauptmenue fuegt die Mod einen `BROGETHER`-Button hinzu. Alternativ oeffnet `F5` dasselbe Modal. Das Modal verhaelt sich bewusst wie Brotatos F8-Feedback-Fenster: es pausiert, legt sich ueber das Spiel und kann mit `Escape`, `F5` oder `Close` geschlossen werden.
|
||||||
|
|
||||||
|
Die UI hat jetzt einen ersten Lobby-Browser. Dort kann man Lobbys suchen, eine Lobby mit optionalem Passwort erstellen und einer ausgewaehlten Lobby beitreten. Nach Host/Join wechselt Brogether in einen Wartebereich mit Steam-Namen und statischen Initialen-Avataren fuer die Lobby-Spieler.
|
||||||
|
|
||||||
## Debug-Hotkeys im Spiel
|
## Debug-Hotkeys im Spiel
|
||||||
|
|
||||||
- `F5`: Brogether-Modal ein-/ausblenden
|
- `F5`: Brogether-Modal ein-/ausblenden
|
||||||
|
|||||||
@@ -2,10 +2,16 @@ extends Node
|
|||||||
|
|
||||||
const MOD_ID := "Brogether-Brogether"
|
const MOD_ID := "Brogether-Brogether"
|
||||||
const LOG_PATH := "user://brogether.log"
|
const LOG_PATH := "user://brogether.log"
|
||||||
|
const BROGETHER_VERSION := "0.1.0"
|
||||||
|
const MAX_LOBBY_MEMBERS := 2
|
||||||
|
|
||||||
var steam = null
|
var steam = null
|
||||||
var lobby_id := 0
|
var lobby_id := 0
|
||||||
var lobby_members := []
|
var lobby_members := []
|
||||||
|
var available_lobbies := []
|
||||||
|
var selected_lobby_id := 0
|
||||||
|
var pending_lobby_name := ""
|
||||||
|
var pending_lobby_password_hash := ""
|
||||||
var steam_id := 0
|
var steam_id := 0
|
||||||
var is_host := false
|
var is_host := false
|
||||||
var packet_sequence := 0
|
var packet_sequence := 0
|
||||||
@@ -16,6 +22,18 @@ var panel: PanelContainer = null
|
|||||||
var panel_status_label: Label = null
|
var panel_status_label: Label = null
|
||||||
var panel_log_label: Label = null
|
var panel_log_label: Label = null
|
||||||
var panel_default_focus: Control = null
|
var panel_default_focus: Control = null
|
||||||
|
var browser_view: Control = null
|
||||||
|
var lobby_view: Control = null
|
||||||
|
var lobby_list_container: VBoxContainer = null
|
||||||
|
var lobby_empty_label: Label = null
|
||||||
|
var lobby_name_input: LineEdit = null
|
||||||
|
var lobby_password_input: LineEdit = null
|
||||||
|
var lobby_password_check: CheckBox = null
|
||||||
|
var join_password_input: LineEdit = null
|
||||||
|
var selected_lobby_label: Label = null
|
||||||
|
var lobby_members_container: GridContainer = null
|
||||||
|
var lobby_title_label: Label = null
|
||||||
|
var lobby_detail_label: Label = null
|
||||||
var status_time_left := 0.0
|
var status_time_left := 0.0
|
||||||
var restore_paused_value := false
|
var restore_paused_value := false
|
||||||
var restore_focus_control = null
|
var restore_focus_control = null
|
||||||
@@ -82,7 +100,7 @@ func _input(event: InputEvent) -> void:
|
|||||||
get_tree().set_input_as_handled()
|
get_tree().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
func create_lobby() -> void:
|
func create_lobby(password: String = "", lobby_name: String = "") -> void:
|
||||||
if steam == null:
|
if steam == null:
|
||||||
_log("cannot create lobby: Steam singleton missing")
|
_log("cannot create lobby: Steam singleton missing")
|
||||||
return
|
return
|
||||||
@@ -90,20 +108,27 @@ func create_lobby() -> void:
|
|||||||
_log("already in lobby %s" % lobby_id)
|
_log("already in lobby %s" % lobby_id)
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
return
|
return
|
||||||
|
pending_lobby_name = lobby_name.strip_edges()
|
||||||
|
if pending_lobby_name.empty():
|
||||||
|
pending_lobby_name = "%s's Brogether Lobby" % _get_persona_name(steam_id)
|
||||||
|
pending_lobby_password_hash = _hash_password(password)
|
||||||
var lobby_type := 2
|
var lobby_type := 2
|
||||||
_log("creating public dev lobby")
|
_log("creating lobby '%s'" % pending_lobby_name)
|
||||||
steam.call("createLobby", lobby_type, 2)
|
steam.call("createLobby", lobby_type, MAX_LOBBY_MEMBERS)
|
||||||
|
|
||||||
|
|
||||||
func find_lobbies() -> void:
|
func find_lobbies() -> void:
|
||||||
if steam == null:
|
if steam == null:
|
||||||
_log("cannot find lobbies: Steam singleton missing")
|
_log("cannot find lobbies: Steam singleton missing")
|
||||||
return
|
return
|
||||||
|
available_lobbies.clear()
|
||||||
|
selected_lobby_id = 0
|
||||||
|
_update_lobby_browser()
|
||||||
if steam.has_method("addRequestLobbyListStringFilter"):
|
if steam.has_method("addRequestLobbyListStringFilter"):
|
||||||
steam.call("addRequestLobbyListStringFilter", "brogether", "1", 0)
|
steam.call("addRequestLobbyListStringFilter", "brogether", "1", 0)
|
||||||
if steam.has_method("addRequestLobbyListDistanceFilter"):
|
if steam.has_method("addRequestLobbyListDistanceFilter"):
|
||||||
steam.call("addRequestLobbyListDistanceFilter", 2)
|
steam.call("addRequestLobbyListDistanceFilter", 2)
|
||||||
_log("requesting lobby list")
|
_log("refreshing lobby browser")
|
||||||
steam.call("requestLobbyList")
|
steam.call("requestLobbyList")
|
||||||
|
|
||||||
|
|
||||||
@@ -125,6 +150,7 @@ func leave_lobby() -> void:
|
|||||||
lobby_id = 0
|
lobby_id = 0
|
||||||
lobby_members.clear()
|
lobby_members.clear()
|
||||||
is_host = false
|
is_host = false
|
||||||
|
_show_browser_view()
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
|
|
||||||
|
|
||||||
@@ -253,10 +279,14 @@ func _on_lobby_created(connect: int, created_lobby_id: int) -> void:
|
|||||||
is_host = true
|
is_host = true
|
||||||
_log("created lobby %s" % lobby_id)
|
_log("created lobby %s" % lobby_id)
|
||||||
steam.call("setLobbyJoinable", lobby_id, true)
|
steam.call("setLobbyJoinable", lobby_id, true)
|
||||||
steam.call("setLobbyData", lobby_id, "name", "Brogether")
|
steam.call("setLobbyData", lobby_id, "name", pending_lobby_name)
|
||||||
steam.call("setLobbyData", lobby_id, "brogether", "1")
|
steam.call("setLobbyData", lobby_id, "brogether", "1")
|
||||||
steam.call("setLobbyData", lobby_id, "version", "0.1.0")
|
steam.call("setLobbyData", lobby_id, "version", BROGETHER_VERSION)
|
||||||
|
steam.call("setLobbyData", lobby_id, "max_players", str(MAX_LOBBY_MEMBERS))
|
||||||
|
steam.call("setLobbyData", lobby_id, "has_password", "1" if not pending_lobby_password_hash.empty() else "0")
|
||||||
|
steam.call("setLobbyData", lobby_id, "password_hash", pending_lobby_password_hash)
|
||||||
_refresh_lobby_members()
|
_refresh_lobby_members()
|
||||||
|
_show_lobby_view()
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
|
|
||||||
|
|
||||||
@@ -270,17 +300,66 @@ func _on_lobby_joined(joined_lobby_id: int, _permissions: int, _locked: bool, re
|
|||||||
is_host = steam.has_method("getLobbyOwner") and int(steam.call("getLobbyOwner", lobby_id)) == steam_id
|
is_host = steam.has_method("getLobbyOwner") and int(steam.call("getLobbyOwner", lobby_id)) == steam_id
|
||||||
_log("joined lobby %s as %s with %s member(s)" % [lobby_id, "host" if is_host else "client", lobby_members.size()])
|
_log("joined lobby %s as %s with %s member(s)" % [lobby_id, "host" if is_host else "client", lobby_members.size()])
|
||||||
_send_p2p_packet(0, {"type": "handshake", "from": steam_id})
|
_send_p2p_packet(0, {"type": "handshake", "from": steam_id})
|
||||||
|
_show_lobby_view()
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
|
|
||||||
|
|
||||||
func _on_lobby_match_list(lobbies: Array) -> void:
|
func _on_lobby_match_list(lobbies: Array) -> void:
|
||||||
_log("found %s lobby candidate(s)" % lobbies.size())
|
available_lobbies.clear()
|
||||||
for candidate in lobbies:
|
for candidate in lobbies:
|
||||||
var candidate_id := int(candidate)
|
var candidate_id := int(candidate)
|
||||||
if steam.has_method("getLobbyData") and steam.call("getLobbyData", candidate_id, "brogether") == "1":
|
if steam.has_method("getLobbyData") and steam.call("getLobbyData", candidate_id, "brogether") == "1":
|
||||||
join_lobby(candidate_id)
|
available_lobbies.append(_get_lobby_summary(candidate_id))
|
||||||
return
|
_log("found %s Brogether lobby candidate(s)" % available_lobbies.size())
|
||||||
_log("no Brogether lobby found")
|
_update_lobby_browser()
|
||||||
|
|
||||||
|
|
||||||
|
func _get_lobby_summary(target_lobby_id: int) -> Dictionary:
|
||||||
|
var member_count := 0
|
||||||
|
var max_members := MAX_LOBBY_MEMBERS
|
||||||
|
if steam != null and steam.has_method("getNumLobbyMembers"):
|
||||||
|
member_count = int(steam.call("getNumLobbyMembers", target_lobby_id))
|
||||||
|
if steam != null and steam.has_method("getLobbyMemberLimit"):
|
||||||
|
max_members = int(steam.call("getLobbyMemberLimit", target_lobby_id))
|
||||||
|
var owner_id := 0
|
||||||
|
if steam != null and steam.has_method("getLobbyOwner"):
|
||||||
|
owner_id = int(steam.call("getLobbyOwner", target_lobby_id))
|
||||||
|
return {
|
||||||
|
"id": target_lobby_id,
|
||||||
|
"name": _get_lobby_data(target_lobby_id, "name", "Brogether Lobby"),
|
||||||
|
"owner_id": owner_id,
|
||||||
|
"owner_name": _get_persona_name(owner_id),
|
||||||
|
"member_count": member_count,
|
||||||
|
"max_members": max_members,
|
||||||
|
"has_password": _get_lobby_data(target_lobby_id, "has_password", "0") == "1",
|
||||||
|
"password_hash": _get_lobby_data(target_lobby_id, "password_hash", ""),
|
||||||
|
"version": _get_lobby_data(target_lobby_id, "version", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _get_lobby_data(target_lobby_id: int, key: String, fallback: String = "") -> String:
|
||||||
|
if steam == null or not steam.has_method("getLobbyData"):
|
||||||
|
return fallback
|
||||||
|
var value = steam.call("getLobbyData", target_lobby_id, key)
|
||||||
|
if value == null:
|
||||||
|
return fallback
|
||||||
|
var text := str(value)
|
||||||
|
return fallback if text.empty() else text
|
||||||
|
|
||||||
|
|
||||||
|
func _get_persona_name(target_steam_id: int) -> String:
|
||||||
|
if target_steam_id == 0:
|
||||||
|
return "Unknown"
|
||||||
|
if steam != null and steam.has_method("getFriendPersonaName"):
|
||||||
|
var persona := str(steam.call("getFriendPersonaName", target_steam_id))
|
||||||
|
if not persona.empty():
|
||||||
|
return persona
|
||||||
|
return str(target_steam_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _hash_password(password: String) -> String:
|
||||||
|
var clean := password.strip_edges()
|
||||||
|
return "" if clean.empty() else str(clean.hash())
|
||||||
|
|
||||||
|
|
||||||
func _on_lobby_chat_update(this_lobby_id: int, _changed_id: int, _making_change_id: int, _chat_state: int) -> void:
|
func _on_lobby_chat_update(this_lobby_id: int, _changed_id: int, _making_change_id: int, _chat_state: int) -> void:
|
||||||
@@ -308,11 +387,9 @@ func _refresh_lobby_members() -> void:
|
|||||||
var member_count := int(steam.call("getNumLobbyMembers", lobby_id))
|
var member_count := int(steam.call("getNumLobbyMembers", lobby_id))
|
||||||
for index in range(member_count):
|
for index in range(member_count):
|
||||||
var member_id := int(steam.call("getLobbyMemberByIndex", lobby_id, index))
|
var member_id := int(steam.call("getLobbyMemberByIndex", lobby_id, index))
|
||||||
var persona := str(member_id)
|
lobby_members.append({"steam_id": member_id, "name": _get_persona_name(member_id)})
|
||||||
if steam.has_method("getFriendPersonaName"):
|
|
||||||
persona = steam.call("getFriendPersonaName", member_id)
|
|
||||||
lobby_members.append({"steam_id": member_id, "name": persona})
|
|
||||||
_log("members: %s" % lobby_members)
|
_log("members: %s" % lobby_members)
|
||||||
|
_update_lobby_room()
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
|
|
||||||
|
|
||||||
@@ -452,72 +529,441 @@ func _create_debug_panel() -> void:
|
|||||||
panel.anchor_right = 0.5
|
panel.anchor_right = 0.5
|
||||||
panel.anchor_top = 0.5
|
panel.anchor_top = 0.5
|
||||||
panel.anchor_bottom = 0.5
|
panel.anchor_bottom = 0.5
|
||||||
panel.margin_left = -260
|
panel.margin_left = -430
|
||||||
panel.margin_right = 260
|
panel.margin_right = 430
|
||||||
panel.margin_top = -170
|
panel.margin_top = -300
|
||||||
panel.margin_bottom = 170
|
panel.margin_bottom = 300
|
||||||
panel.pause_mode = Node.PAUSE_MODE_PROCESS
|
panel.pause_mode = Node.PAUSE_MODE_PROCESS
|
||||||
panel.visible = false
|
panel.visible = false
|
||||||
|
|
||||||
var style: StyleBoxFlat = StyleBoxFlat.new()
|
panel.add_stylebox_override("panel", _make_panel_style(Color(0.075, 0.08, 0.09, 0.98), Color(0.55, 0.58, 0.62, 1.0), 18))
|
||||||
style.bg_color = Color(0.08, 0.09, 0.1, 0.96)
|
|
||||||
style.border_color = Color(0.55, 0.58, 0.62, 1.0)
|
|
||||||
style.set_border_width_all(1)
|
|
||||||
style.set_corner_radius_all(3)
|
|
||||||
style.content_margin_left = 18
|
|
||||||
style.content_margin_right = 18
|
|
||||||
style.content_margin_top = 16
|
|
||||||
style.content_margin_bottom = 16
|
|
||||||
panel.add_stylebox_override("panel", style)
|
|
||||||
|
|
||||||
var box: VBoxContainer = VBoxContainer.new()
|
var box: VBoxContainer = VBoxContainer.new()
|
||||||
box.name = "Content"
|
box.name = "Content"
|
||||||
box.add_constant_override("separation", 10)
|
box.add_constant_override("separation", 12)
|
||||||
panel.add_child(box)
|
panel.add_child(box)
|
||||||
|
|
||||||
|
var header: HBoxContainer = HBoxContainer.new()
|
||||||
|
header.add_constant_override("separation", 10)
|
||||||
|
box.add_child(header)
|
||||||
|
|
||||||
var title: Label = Label.new()
|
var title: Label = Label.new()
|
||||||
title.text = "BROGETHER"
|
title.text = "BROGETHER"
|
||||||
title.align = Label.ALIGN_CENTER
|
title.rect_min_size = Vector2(220, 28)
|
||||||
title.add_color_override("font_color", Color(1, 1, 1, 1))
|
title.add_color_override("font_color", Color(1, 1, 1, 1))
|
||||||
box.add_child(title)
|
header.add_child(title)
|
||||||
|
|
||||||
panel_status_label = Label.new()
|
panel_status_label = Label.new()
|
||||||
panel_status_label.text = "Steam: ..."
|
panel_status_label.text = "Steam: ..."
|
||||||
|
panel_status_label.align = Label.ALIGN_RIGHT
|
||||||
panel_status_label.autowrap = true
|
panel_status_label.autowrap = true
|
||||||
box.add_child(panel_status_label)
|
panel_status_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
header.add_child(panel_status_label)
|
||||||
|
|
||||||
var grid: GridContainer = GridContainer.new()
|
var close_button: Button = Button.new()
|
||||||
grid.columns = 2
|
close_button.text = "X"
|
||||||
grid.add_constant_override("hseparation", 6)
|
close_button.rect_min_size = Vector2(42, 34)
|
||||||
grid.add_constant_override("vseparation", 6)
|
close_button.connect("pressed", self, "close_debug_panel")
|
||||||
box.add_child(grid)
|
header.add_child(close_button)
|
||||||
|
|
||||||
panel_default_focus = _add_panel_button(grid, "Host", "create_lobby")
|
var separator: HSeparator = HSeparator.new()
|
||||||
_add_panel_button(grid, "Join", "find_lobbies")
|
box.add_child(separator)
|
||||||
_add_panel_button(grid, "Leave", "leave_lobby")
|
|
||||||
_add_panel_button(grid, "Ping", "send_ping")
|
browser_view = HBoxContainer.new()
|
||||||
_add_panel_button(grid, "Loopback", "run_loopback_test")
|
browser_view.name = "LobbyBrowser"
|
||||||
_add_panel_button(grid, "Close", "close_debug_panel")
|
browser_view.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
browser_view.add_constant_override("separation", 14)
|
||||||
|
box.add_child(browser_view)
|
||||||
|
|
||||||
|
var browser_column: VBoxContainer = VBoxContainer.new()
|
||||||
|
browser_column.rect_min_size = Vector2(390, 410)
|
||||||
|
browser_column.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
browser_column.add_constant_override("separation", 8)
|
||||||
|
browser_view.add_child(browser_column)
|
||||||
|
|
||||||
|
var browser_title: Label = Label.new()
|
||||||
|
browser_title.text = "LOBBY BROWSER"
|
||||||
|
browser_title.add_color_override("font_color", Color(0.92, 0.96, 1.0, 1.0))
|
||||||
|
browser_column.add_child(browser_title)
|
||||||
|
|
||||||
|
var browser_hint: Label = Label.new()
|
||||||
|
browser_hint.text = "Offene Brogether-Lobbys werden hier angezeigt."
|
||||||
|
browser_hint.autowrap = true
|
||||||
|
browser_column.add_child(browser_hint)
|
||||||
|
|
||||||
|
var scroll: ScrollContainer = ScrollContainer.new()
|
||||||
|
scroll.rect_min_size = Vector2(390, 300)
|
||||||
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
browser_column.add_child(scroll)
|
||||||
|
|
||||||
|
lobby_list_container = VBoxContainer.new()
|
||||||
|
lobby_list_container.add_constant_override("separation", 6)
|
||||||
|
scroll.add_child(lobby_list_container)
|
||||||
|
|
||||||
|
lobby_empty_label = Label.new()
|
||||||
|
lobby_empty_label.text = "Noch keine Lobbys geladen."
|
||||||
|
lobby_empty_label.autowrap = true
|
||||||
|
lobby_list_container.add_child(lobby_empty_label)
|
||||||
|
|
||||||
|
var refresh_button: Button = Button.new()
|
||||||
|
refresh_button.text = "Refresh"
|
||||||
|
refresh_button.rect_min_size = Vector2(390, 36)
|
||||||
|
refresh_button.connect("pressed", self, "find_lobbies")
|
||||||
|
browser_column.add_child(refresh_button)
|
||||||
|
|
||||||
|
var action_column: VBoxContainer = VBoxContainer.new()
|
||||||
|
action_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
action_column.add_constant_override("separation", 10)
|
||||||
|
browser_view.add_child(action_column)
|
||||||
|
|
||||||
|
var create_box: PanelContainer = PanelContainer.new()
|
||||||
|
create_box.add_stylebox_override("panel", _make_panel_style(Color(0.105, 0.11, 0.12, 0.95), Color(0.28, 0.31, 0.34, 1.0), 12))
|
||||||
|
action_column.add_child(create_box)
|
||||||
|
|
||||||
|
var create_content: VBoxContainer = VBoxContainer.new()
|
||||||
|
create_content.add_constant_override("separation", 8)
|
||||||
|
create_box.add_child(create_content)
|
||||||
|
|
||||||
|
var create_title: Label = Label.new()
|
||||||
|
create_title.text = "LOBBY ERSTELLEN"
|
||||||
|
create_content.add_child(create_title)
|
||||||
|
|
||||||
|
lobby_name_input = LineEdit.new()
|
||||||
|
lobby_name_input.placeholder_text = "Lobbyname"
|
||||||
|
lobby_name_input.rect_min_size = Vector2(360, 34)
|
||||||
|
create_content.add_child(lobby_name_input)
|
||||||
|
|
||||||
|
lobby_password_check = CheckBox.new()
|
||||||
|
lobby_password_check.text = "Passwort verwenden"
|
||||||
|
create_content.add_child(lobby_password_check)
|
||||||
|
|
||||||
|
lobby_password_input = LineEdit.new()
|
||||||
|
lobby_password_input.placeholder_text = "Passwort"
|
||||||
|
lobby_password_input.secret = true
|
||||||
|
lobby_password_input.rect_min_size = Vector2(360, 34)
|
||||||
|
create_content.add_child(lobby_password_input)
|
||||||
|
|
||||||
|
var host_button: Button = Button.new()
|
||||||
|
host_button.text = "Host Lobby"
|
||||||
|
host_button.rect_min_size = Vector2(360, 38)
|
||||||
|
host_button.connect("pressed", self, "_create_lobby_from_ui")
|
||||||
|
create_content.add_child(host_button)
|
||||||
|
panel_default_focus = host_button
|
||||||
|
|
||||||
|
var join_box: PanelContainer = PanelContainer.new()
|
||||||
|
join_box.add_stylebox_override("panel", _make_panel_style(Color(0.105, 0.11, 0.12, 0.95), Color(0.28, 0.31, 0.34, 1.0), 12))
|
||||||
|
action_column.add_child(join_box)
|
||||||
|
|
||||||
|
var join_content: VBoxContainer = VBoxContainer.new()
|
||||||
|
join_content.add_constant_override("separation", 8)
|
||||||
|
join_box.add_child(join_content)
|
||||||
|
|
||||||
|
var join_title: Label = Label.new()
|
||||||
|
join_title.text = "AUSGEWAEHLTE LOBBY"
|
||||||
|
join_content.add_child(join_title)
|
||||||
|
|
||||||
|
selected_lobby_label = Label.new()
|
||||||
|
selected_lobby_label.text = "Keine Lobby ausgewaehlt."
|
||||||
|
selected_lobby_label.autowrap = true
|
||||||
|
selected_lobby_label.rect_min_size = Vector2(360, 58)
|
||||||
|
join_content.add_child(selected_lobby_label)
|
||||||
|
|
||||||
|
join_password_input = LineEdit.new()
|
||||||
|
join_password_input.placeholder_text = "Passwort, falls noetig"
|
||||||
|
join_password_input.secret = true
|
||||||
|
join_password_input.rect_min_size = Vector2(360, 34)
|
||||||
|
join_content.add_child(join_password_input)
|
||||||
|
|
||||||
|
var join_button: Button = Button.new()
|
||||||
|
join_button.text = "Join Lobby"
|
||||||
|
join_button.rect_min_size = Vector2(360, 38)
|
||||||
|
join_button.connect("pressed", self, "_join_selected_lobby_from_ui")
|
||||||
|
join_content.add_child(join_button)
|
||||||
|
|
||||||
|
lobby_view = VBoxContainer.new()
|
||||||
|
lobby_view.name = "LobbyRoom"
|
||||||
|
lobby_view.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
lobby_view.add_constant_override("separation", 12)
|
||||||
|
lobby_view.visible = false
|
||||||
|
box.add_child(lobby_view)
|
||||||
|
|
||||||
|
lobby_title_label = Label.new()
|
||||||
|
lobby_title_label.text = "WARTEBEREICH"
|
||||||
|
lobby_title_label.add_color_override("font_color", Color(0.92, 0.96, 1.0, 1.0))
|
||||||
|
lobby_view.add_child(lobby_title_label)
|
||||||
|
|
||||||
|
lobby_detail_label = Label.new()
|
||||||
|
lobby_detail_label.text = "Noch nicht in einer Lobby."
|
||||||
|
lobby_detail_label.autowrap = true
|
||||||
|
lobby_view.add_child(lobby_detail_label)
|
||||||
|
|
||||||
|
lobby_members_container = GridContainer.new()
|
||||||
|
lobby_members_container.columns = 2
|
||||||
|
lobby_members_container.add_constant_override("hseparation", 10)
|
||||||
|
lobby_members_container.add_constant_override("vseparation", 10)
|
||||||
|
lobby_members_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
lobby_view.add_child(lobby_members_container)
|
||||||
|
|
||||||
|
var lobby_actions: HBoxContainer = HBoxContainer.new()
|
||||||
|
lobby_actions.add_constant_override("separation", 8)
|
||||||
|
lobby_view.add_child(lobby_actions)
|
||||||
|
|
||||||
|
_add_panel_button(lobby_actions, "Ping", "send_ping")
|
||||||
|
_add_panel_button(lobby_actions, "Leave", "leave_lobby")
|
||||||
|
_add_panel_button(lobby_actions, "Browser", "_show_browser_view")
|
||||||
|
|
||||||
panel_log_label = Label.new()
|
panel_log_label = Label.new()
|
||||||
panel_log_label.text = "Ready"
|
panel_log_label.text = "Ready"
|
||||||
panel_log_label.autowrap = true
|
panel_log_label.autowrap = true
|
||||||
panel_log_label.rect_min_size = Vector2(480, 78)
|
panel_log_label.rect_min_size = Vector2(820, 44)
|
||||||
box.add_child(panel_log_label)
|
box.add_child(panel_log_label)
|
||||||
|
|
||||||
status_layer.add_child(panel)
|
status_layer.add_child(panel)
|
||||||
|
_show_browser_view()
|
||||||
_update_debug_panel()
|
_update_debug_panel()
|
||||||
|
|
||||||
|
|
||||||
func _add_panel_button(parent: Control, label: String, method_name: String) -> Button:
|
func _add_panel_button(parent: Control, label: String, method_name: String) -> Button:
|
||||||
var button: Button = Button.new()
|
var button: Button = Button.new()
|
||||||
button.text = label
|
button.text = label
|
||||||
button.rect_min_size = Vector2(236, 34)
|
button.rect_min_size = Vector2(130, 36)
|
||||||
button.connect("pressed", self, method_name)
|
button.connect("pressed", self, method_name)
|
||||||
parent.add_child(button)
|
parent.add_child(button)
|
||||||
return button
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
func _make_panel_style(bg: Color, border: Color, margin: int) -> StyleBoxFlat:
|
||||||
|
var style: StyleBoxFlat = StyleBoxFlat.new()
|
||||||
|
style.bg_color = bg
|
||||||
|
style.border_color = border
|
||||||
|
style.set_border_width_all(1)
|
||||||
|
style.set_corner_radius_all(3)
|
||||||
|
style.content_margin_left = margin
|
||||||
|
style.content_margin_right = margin
|
||||||
|
style.content_margin_top = margin
|
||||||
|
style.content_margin_bottom = margin
|
||||||
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
func _create_lobby_from_ui() -> void:
|
||||||
|
var password := ""
|
||||||
|
if lobby_password_check != null and lobby_password_check.pressed and lobby_password_input != null:
|
||||||
|
password = lobby_password_input.text
|
||||||
|
var display_name := ""
|
||||||
|
if lobby_name_input != null:
|
||||||
|
display_name = lobby_name_input.text
|
||||||
|
create_lobby(password, display_name)
|
||||||
|
|
||||||
|
|
||||||
|
func _join_selected_lobby_from_ui() -> void:
|
||||||
|
if selected_lobby_id == 0:
|
||||||
|
_log("select a lobby before joining")
|
||||||
|
return
|
||||||
|
var summary: Dictionary = _get_selected_lobby_summary()
|
||||||
|
if summary.has("has_password") and bool(summary["has_password"]):
|
||||||
|
var entered := ""
|
||||||
|
if join_password_input != null:
|
||||||
|
entered = join_password_input.text
|
||||||
|
if _hash_password(entered) != str(summary.get("password_hash", "")):
|
||||||
|
_log("wrong or missing lobby password")
|
||||||
|
return
|
||||||
|
join_lobby(selected_lobby_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _show_browser_view() -> void:
|
||||||
|
if browser_view != null:
|
||||||
|
browser_view.visible = true
|
||||||
|
if lobby_view != null:
|
||||||
|
lobby_view.visible = false
|
||||||
|
_update_lobby_browser()
|
||||||
|
|
||||||
|
|
||||||
|
func _show_lobby_view() -> void:
|
||||||
|
if browser_view != null:
|
||||||
|
browser_view.visible = false
|
||||||
|
if lobby_view != null:
|
||||||
|
lobby_view.visible = true
|
||||||
|
_update_lobby_room()
|
||||||
|
|
||||||
|
|
||||||
|
func _update_lobby_browser() -> void:
|
||||||
|
if lobby_list_container == null:
|
||||||
|
return
|
||||||
|
_clear_children(lobby_list_container)
|
||||||
|
if available_lobbies.empty():
|
||||||
|
lobby_empty_label = Label.new()
|
||||||
|
lobby_empty_label.text = "Keine Brogether-Lobbys gefunden.\nErstelle eine neue Lobby oder druecke Refresh."
|
||||||
|
lobby_empty_label.autowrap = true
|
||||||
|
lobby_empty_label.rect_min_size = Vector2(360, 80)
|
||||||
|
lobby_list_container.add_child(lobby_empty_label)
|
||||||
|
else:
|
||||||
|
for summary_data in available_lobbies:
|
||||||
|
var summary: Dictionary = summary_data
|
||||||
|
var button: Button = Button.new()
|
||||||
|
button.text = _format_lobby_summary(summary)
|
||||||
|
button.rect_min_size = Vector2(360, 74)
|
||||||
|
button.connect("pressed", self, "_on_lobby_row_pressed", [int(summary["id"])])
|
||||||
|
lobby_list_container.add_child(button)
|
||||||
|
_update_selected_lobby_label()
|
||||||
|
|
||||||
|
|
||||||
|
func _format_lobby_summary(summary: Dictionary) -> String:
|
||||||
|
var lock_text := "PASSWORT" if bool(summary.get("has_password", false)) else "OFFEN"
|
||||||
|
return "%s\n%s/%s Spieler | %s | Host: %s" % [
|
||||||
|
str(summary.get("name", "Brogether Lobby")),
|
||||||
|
str(summary.get("member_count", 0)),
|
||||||
|
str(summary.get("max_members", MAX_LOBBY_MEMBERS)),
|
||||||
|
lock_text,
|
||||||
|
str(summary.get("owner_name", "Unknown"))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func _on_lobby_row_pressed(target_lobby_id: int) -> void:
|
||||||
|
selected_lobby_id = target_lobby_id
|
||||||
|
_update_selected_lobby_label()
|
||||||
|
|
||||||
|
|
||||||
|
func _update_selected_lobby_label() -> void:
|
||||||
|
if selected_lobby_label == null:
|
||||||
|
return
|
||||||
|
var summary: Dictionary = _get_selected_lobby_summary()
|
||||||
|
if summary.empty():
|
||||||
|
selected_lobby_label.text = "Keine Lobby ausgewaehlt."
|
||||||
|
return
|
||||||
|
var password_text := "Passwort noetig" if bool(summary.get("has_password", false)) else "Kein Passwort"
|
||||||
|
selected_lobby_label.text = "%s\n%s/%s Spieler\n%s" % [
|
||||||
|
str(summary.get("name", "Brogether Lobby")),
|
||||||
|
str(summary.get("member_count", 0)),
|
||||||
|
str(summary.get("max_members", MAX_LOBBY_MEMBERS)),
|
||||||
|
password_text
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func _get_selected_lobby_summary() -> Dictionary:
|
||||||
|
for summary_data in available_lobbies:
|
||||||
|
var summary: Dictionary = summary_data
|
||||||
|
if int(summary.get("id", 0)) == selected_lobby_id:
|
||||||
|
return summary
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
func _update_lobby_room() -> void:
|
||||||
|
if lobby_members_container == null:
|
||||||
|
return
|
||||||
|
_clear_children(lobby_members_container)
|
||||||
|
if lobby_id == 0:
|
||||||
|
if lobby_title_label != null:
|
||||||
|
lobby_title_label.text = "WARTEBEREICH"
|
||||||
|
if lobby_detail_label != null:
|
||||||
|
lobby_detail_label.text = "Noch nicht in einer Lobby."
|
||||||
|
return
|
||||||
|
var lobby_name: String = _get_lobby_data(lobby_id, "name", "Brogether Lobby")
|
||||||
|
var password_text := "mit Passwort" if _get_lobby_data(lobby_id, "has_password", "0") == "1" else "ohne Passwort"
|
||||||
|
if lobby_title_label != null:
|
||||||
|
lobby_title_label.text = lobby_name.to_upper()
|
||||||
|
if lobby_detail_label != null:
|
||||||
|
lobby_detail_label.text = "Wartebereich %s | %s/%s Spieler | %s" % [str(lobby_id), lobby_members.size(), MAX_LOBBY_MEMBERS, password_text]
|
||||||
|
for member in lobby_members:
|
||||||
|
_add_member_card(member)
|
||||||
|
var empty_slot_count: int = max(0, MAX_LOBBY_MEMBERS - lobby_members.size())
|
||||||
|
for empty_slot in range(empty_slot_count):
|
||||||
|
_add_empty_member_slot(empty_slot)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_member_card(member: Dictionary) -> void:
|
||||||
|
var card: PanelContainer = PanelContainer.new()
|
||||||
|
card.rect_min_size = Vector2(390, 128)
|
||||||
|
card.add_stylebox_override("panel", _make_panel_style(Color(0.12, 0.13, 0.14, 0.96), Color(0.32, 0.36, 0.39, 1.0), 10))
|
||||||
|
lobby_members_container.add_child(card)
|
||||||
|
|
||||||
|
var row: HBoxContainer = HBoxContainer.new()
|
||||||
|
row.add_constant_override("separation", 12)
|
||||||
|
card.add_child(row)
|
||||||
|
|
||||||
|
var avatar: PanelContainer = PanelContainer.new()
|
||||||
|
avatar.rect_min_size = Vector2(74, 74)
|
||||||
|
avatar.add_stylebox_override("panel", _make_panel_style(_avatar_color(int(member.get("steam_id", 0))), Color(0.72, 0.76, 0.8, 1.0), 4))
|
||||||
|
row.add_child(avatar)
|
||||||
|
|
||||||
|
var initials: Label = Label.new()
|
||||||
|
initials.text = _initials(str(member.get("name", "?")))
|
||||||
|
initials.align = Label.ALIGN_CENTER
|
||||||
|
initials.valign = Label.VALIGN_CENTER
|
||||||
|
initials.rect_min_size = Vector2(66, 66)
|
||||||
|
initials.add_color_override("font_color", Color(1, 1, 1, 1))
|
||||||
|
avatar.add_child(initials)
|
||||||
|
|
||||||
|
var text_box: VBoxContainer = VBoxContainer.new()
|
||||||
|
text_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
row.add_child(text_box)
|
||||||
|
|
||||||
|
var name_label: Label = Label.new()
|
||||||
|
name_label.text = str(member.get("name", "Unknown"))
|
||||||
|
name_label.add_color_override("font_color", Color(0.95, 0.97, 1.0, 1.0))
|
||||||
|
text_box.add_child(name_label)
|
||||||
|
|
||||||
|
var id_label: Label = Label.new()
|
||||||
|
id_label.text = "Steam %s" % str(member.get("steam_id", 0))
|
||||||
|
text_box.add_child(id_label)
|
||||||
|
|
||||||
|
var role_label: Label = Label.new()
|
||||||
|
role_label.text = "Host" if int(member.get("steam_id", 0)) == _current_lobby_owner() else "Player"
|
||||||
|
text_box.add_child(role_label)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_empty_member_slot(_index: int) -> void:
|
||||||
|
var card: PanelContainer = PanelContainer.new()
|
||||||
|
card.rect_min_size = Vector2(390, 128)
|
||||||
|
card.add_stylebox_override("panel", _make_panel_style(Color(0.09, 0.095, 0.105, 0.72), Color(0.2, 0.22, 0.24, 1.0), 10))
|
||||||
|
lobby_members_container.add_child(card)
|
||||||
|
|
||||||
|
var label: Label = Label.new()
|
||||||
|
label.text = "Freier Platz"
|
||||||
|
label.align = Label.ALIGN_CENTER
|
||||||
|
label.valign = Label.VALIGN_CENTER
|
||||||
|
label.rect_min_size = Vector2(360, 96)
|
||||||
|
card.add_child(label)
|
||||||
|
|
||||||
|
|
||||||
|
func _current_lobby_owner() -> int:
|
||||||
|
if steam != null and lobby_id != 0 and steam.has_method("getLobbyOwner"):
|
||||||
|
return int(steam.call("getLobbyOwner", lobby_id))
|
||||||
|
return steam_id if is_host else 0
|
||||||
|
|
||||||
|
|
||||||
|
func _initials(display_name: String) -> String:
|
||||||
|
var clean := display_name.strip_edges()
|
||||||
|
if clean.empty():
|
||||||
|
return "?"
|
||||||
|
var result := ""
|
||||||
|
var parts := clean.split(" ", false)
|
||||||
|
for part in parts:
|
||||||
|
if result.length() >= 2:
|
||||||
|
break
|
||||||
|
if str(part).length() > 0:
|
||||||
|
result += str(part).substr(0, 1).to_upper()
|
||||||
|
if result.empty():
|
||||||
|
result = clean.substr(0, 1).to_upper()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _avatar_color(value: int) -> Color:
|
||||||
|
var colors: Array = [
|
||||||
|
Color(0.25, 0.45, 0.82, 1.0),
|
||||||
|
Color(0.68, 0.35, 0.72, 1.0),
|
||||||
|
Color(0.25, 0.58, 0.48, 1.0),
|
||||||
|
Color(0.74, 0.44, 0.28, 1.0)
|
||||||
|
]
|
||||||
|
var color_index: int = int(abs(value)) % colors.size()
|
||||||
|
return colors[color_index]
|
||||||
|
|
||||||
|
|
||||||
|
func _clear_children(parent: Node) -> void:
|
||||||
|
for child in parent.get_children():
|
||||||
|
parent.remove_child(child)
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
|
||||||
func toggle_debug_panel() -> void:
|
func toggle_debug_panel() -> void:
|
||||||
if panel != null and panel.visible:
|
if panel != null and panel.visible:
|
||||||
close_debug_panel()
|
close_debug_panel()
|
||||||
@@ -535,6 +981,10 @@ func open_debug_panel() -> void:
|
|||||||
dim_background.visible = true
|
dim_background.visible = true
|
||||||
panel.visible = true
|
panel.visible = true
|
||||||
set_process_input(true)
|
set_process_input(true)
|
||||||
|
if lobby_id != 0:
|
||||||
|
_show_lobby_view()
|
||||||
|
else:
|
||||||
|
_show_browser_view()
|
||||||
if panel_default_focus != null and is_instance_valid(panel_default_focus):
|
if panel_default_focus != null and is_instance_valid(panel_default_focus):
|
||||||
panel_default_focus.call_deferred("grab_focus")
|
panel_default_focus.call_deferred("grab_focus")
|
||||||
_update_debug_panel("Panel opened")
|
_update_debug_panel("Panel opened")
|
||||||
|
|||||||
Reference in New Issue
Block a user