Add game mode categories, character creation, on-foot ship/hub mode, RAM currency, and in-ship navigation
Bundles several sessions' worth of previously uncommitted work: map categories with Domination/Conquest/King of the Hill mode stubs and a server-list mode filter; a procedurally-drawn character-creation screen replacing the old callsign-only PROFILE overlay; the on-foot groundwork (walkable station hub, ship interior, character controller) plus the RAM currency backend; and today's addition, an in-ship Helldivers-2-style navigation table that QUICK PLAY's queue/connect flow and the Belters/ Military hub travel now live behind, with hub-and-ship return paths and a context-aware pause menu usable both in-match and inside the ship. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+120
-17
@@ -17,7 +17,10 @@ const PING_COLOR_GOOD := Color(0.4, 0.9, 0.5)
|
||||
const PING_COLOR_OK := Color(0.95, 0.75, 0.3)
|
||||
const PING_COLOR_BAD := Color(0.9, 0.4, 0.4)
|
||||
|
||||
var _servers: Array = []
|
||||
var _all_servers: Array = [] # every row from the last GET /servers, unfiltered
|
||||
var _servers: Array = [] # _all_servers narrowed to _selected_category -- what's actually rendered/connectable
|
||||
var _selected_category: String = "" # "" = ALL MODES; otherwise one of GameConfig.GAME_MODE_IDS
|
||||
var _category_group: ButtonGroup
|
||||
var _selected_index: int = -1
|
||||
var _row_group: ButtonGroup
|
||||
var _list_vbox: VBoxContainer
|
||||
@@ -64,10 +67,78 @@ func _process(_delta: float) -> void:
|
||||
func _build_ui() -> void:
|
||||
_add_bg()
|
||||
_add_header()
|
||||
_add_category_filters()
|
||||
_add_list_panel()
|
||||
_add_bottom_bar()
|
||||
|
||||
|
||||
# Game-mode toggle bar (ALL MODES + one per GameConfig.GAME_MODE_IDS) -- a
|
||||
# separate, filterable category from the map/address text each row already
|
||||
# shows, so a player can narrow the list to e.g. just Team Deathmatch servers
|
||||
# instead of reading mode off every row by eye. A ButtonGroup makes these
|
||||
# mutually exclusive (radio-style), same pattern _row_group already uses for
|
||||
# the server rows below.
|
||||
func _add_category_filters() -> void:
|
||||
var bar := HBoxContainer.new()
|
||||
bar.anchor_left = 0.0
|
||||
bar.anchor_top = 0.0
|
||||
bar.anchor_right = 1.0
|
||||
bar.anchor_bottom = 0.0
|
||||
bar.offset_left = 60.0
|
||||
bar.offset_top = 100.0
|
||||
bar.offset_right = -60.0
|
||||
bar.offset_bottom = 130.0
|
||||
bar.add_theme_constant_override("separation", 8)
|
||||
add_child(bar)
|
||||
|
||||
_category_group = ButtonGroup.new()
|
||||
var categories: Array = [""]
|
||||
categories.append_array(GameConfig.GAME_MODE_IDS)
|
||||
for cat_id in categories:
|
||||
bar.add_child(_make_category_button(cat_id))
|
||||
|
||||
|
||||
func _make_category_button(cat_id: String) -> Button:
|
||||
var btn := Button.new()
|
||||
btn.text = "ALL MODES" if cat_id == "" else _format_mode_name(cat_id)
|
||||
btn.toggle_mode = true
|
||||
btn.button_pressed = cat_id == _selected_category
|
||||
btn.button_group = _category_group
|
||||
btn.custom_minimum_size = Vector2(0, 30)
|
||||
btn.add_theme_font_size_override("font_size", 13)
|
||||
|
||||
var sn := StyleBoxFlat.new()
|
||||
sn.bg_color = Color(1.0, 1.0, 1.0, 0.06)
|
||||
sn.set_corner_radius_all(4)
|
||||
sn.content_margin_left = 12.0
|
||||
sn.content_margin_right = 12.0
|
||||
var sh := StyleBoxFlat.new()
|
||||
sh.bg_color = Color(1.0, 1.0, 1.0, 0.14)
|
||||
sh.set_corner_radius_all(4)
|
||||
sh.content_margin_left = 12.0
|
||||
sh.content_margin_right = 12.0
|
||||
var sp := StyleBoxFlat.new()
|
||||
sp.bg_color = Color(1.0, 1.0, 1.0, 0.9)
|
||||
sp.set_corner_radius_all(4)
|
||||
sp.content_margin_left = 12.0
|
||||
sp.content_margin_right = 12.0
|
||||
btn.add_theme_stylebox_override("normal", sn)
|
||||
btn.add_theme_stylebox_override("hover", sh)
|
||||
btn.add_theme_stylebox_override("pressed", sp)
|
||||
btn.add_theme_color_override("font_color", Color(0.85, 0.87, 0.92))
|
||||
btn.add_theme_color_override("font_color_pressed", Color(0.05, 0.05, 0.08))
|
||||
|
||||
btn.pressed.connect(_on_category_selected.bind(cat_id))
|
||||
return btn
|
||||
|
||||
|
||||
func _on_category_selected(cat_id: String) -> void:
|
||||
if _selected_category == cat_id:
|
||||
return
|
||||
_selected_category = cat_id
|
||||
_apply_filter()
|
||||
|
||||
|
||||
func _add_bg() -> void:
|
||||
var bg := TextureRect.new()
|
||||
bg.texture = load("res://assets/images/background/skybox/1.png")
|
||||
@@ -105,9 +176,9 @@ func _add_list_panel() -> void:
|
||||
col_hdr.anchor_right = 1.0
|
||||
col_hdr.anchor_bottom = 0.0
|
||||
col_hdr.offset_left = 60.0
|
||||
col_hdr.offset_top = 108.0
|
||||
col_hdr.offset_top = 142.0
|
||||
col_hdr.offset_right = -60.0
|
||||
col_hdr.offset_bottom = 132.0
|
||||
col_hdr.offset_bottom = 166.0
|
||||
add_child(col_hdr)
|
||||
|
||||
var h_name := _field_label("MODE / ADDRESS")
|
||||
@@ -132,9 +203,9 @@ func _add_list_panel() -> void:
|
||||
sep.anchor_right = 1.0
|
||||
sep.anchor_bottom = 0.0
|
||||
sep.offset_left = 60.0
|
||||
sep.offset_top = 136.0
|
||||
sep.offset_top = 170.0
|
||||
sep.offset_right = -60.0
|
||||
sep.offset_bottom = 140.0
|
||||
sep.offset_bottom = 174.0
|
||||
sep.add_theme_color_override("color", Color(1.0, 1.0, 1.0, 0.2))
|
||||
add_child(sep)
|
||||
|
||||
@@ -144,7 +215,7 @@ func _add_list_panel() -> void:
|
||||
scroll.anchor_right = 1.0
|
||||
scroll.anchor_bottom = 1.0
|
||||
scroll.offset_left = 60.0
|
||||
scroll.offset_top = 148.0
|
||||
scroll.offset_top = 182.0
|
||||
scroll.offset_right = -60.0
|
||||
scroll.offset_bottom = -84.0
|
||||
add_child(scroll)
|
||||
@@ -236,11 +307,28 @@ func _add_bottom_bar() -> void:
|
||||
add_child(_connect_btn)
|
||||
|
||||
|
||||
# Fetches the full unfiltered server list from the API. Re-rendering for a
|
||||
# category toggle (_on_category_selected()) doesn't need to hit the network
|
||||
# again -- that's _apply_filter()'s job, working off _all_servers.
|
||||
func _refresh_servers() -> void:
|
||||
_selected_index = -1
|
||||
_connect_btn.disabled = true
|
||||
_status_lbl.add_theme_color_override("font_color", Color(0.6, 0.72, 0.86))
|
||||
_status_lbl.text = "Loading servers..."
|
||||
|
||||
var result: Dictionary = await MatchmakingClient.list_servers()
|
||||
if not result.ok:
|
||||
_all_servers = []
|
||||
_clear_rows()
|
||||
_status_lbl.add_theme_color_override("font_color", Color(0.85, 0.4, 0.4))
|
||||
_status_lbl.text = "Couldn't reach matchmaking service."
|
||||
return
|
||||
|
||||
_all_servers = result.servers
|
||||
_apply_filter()
|
||||
|
||||
|
||||
func _clear_rows() -> void:
|
||||
_selected_index = -1
|
||||
_connect_btn.disabled = true
|
||||
for child in _list_vbox.get_children():
|
||||
child.queue_free()
|
||||
for probe in _ping_probes:
|
||||
@@ -250,15 +338,22 @@ func _refresh_servers() -> void:
|
||||
_ping_labels.clear()
|
||||
_ping_probes.clear()
|
||||
|
||||
var result: Dictionary = await MatchmakingClient.list_servers()
|
||||
if not result.ok:
|
||||
_status_lbl.add_theme_color_override("font_color", Color(0.85, 0.4, 0.4))
|
||||
_status_lbl.text = "Couldn't reach matchmaking service."
|
||||
return
|
||||
|
||||
_servers = result.servers
|
||||
# Narrows _all_servers down to _selected_category ("" = no filter, show
|
||||
# everything) and rebuilds the row list from that -- the only place _servers
|
||||
# (what rows/ping-probes/CONNECT actually index into) gets assigned.
|
||||
func _apply_filter() -> void:
|
||||
_clear_rows()
|
||||
_status_lbl.add_theme_color_override("font_color", Color(0.6, 0.72, 0.86))
|
||||
|
||||
_servers = _all_servers.filter(
|
||||
func(s): return _selected_category == "" or str(s.get("game_mode", "")) == _selected_category
|
||||
)
|
||||
if _servers.is_empty():
|
||||
_status_lbl.text = "No servers online right now."
|
||||
_status_lbl.text = (
|
||||
"No servers online right now." if _selected_category == ""
|
||||
else "No %s servers online right now." % _format_mode_name(_selected_category)
|
||||
)
|
||||
return
|
||||
|
||||
_status_lbl.text = ""
|
||||
@@ -300,6 +395,13 @@ func _apply_ping_result(index: int, response: String, rtt_msec: int) -> void:
|
||||
ping_lbl.add_theme_color_override("font_color", Color(0.5, 0.53, 0.6))
|
||||
|
||||
|
||||
# "team_deathmatch" -> "TEAM DEATHMATCH" -- matches GameMode.get_mode_name()'s
|
||||
# own formatting (world/game_modes/*.gd) so the server list and the in-match
|
||||
# scoreboard/banner never show two different spellings of the same mode.
|
||||
func _format_mode_name(mode_id: String) -> String:
|
||||
return mode_id.replace("_", " ").to_upper()
|
||||
|
||||
|
||||
func _make_row(server: Dictionary, index: int) -> Button:
|
||||
var row := Button.new()
|
||||
row.toggle_mode = true
|
||||
@@ -324,9 +426,10 @@ func _make_row(server: Dictionary, index: int) -> Button:
|
||||
hb.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
row.add_child(hb)
|
||||
|
||||
var mode: String = str(server.get("mode", "?")).to_upper()
|
||||
var game_mode: String = _format_mode_name(str(server.get("game_mode", "team_deathmatch")))
|
||||
var map_name: String = str(server.get("map_name", "?"))
|
||||
var addr_lbl := Label.new()
|
||||
addr_lbl.text = "%s %s:%s" % [mode, server.get("ip", "?"), str(server.get("port", "?"))]
|
||||
addr_lbl.text = "%s · %s %s:%s" % [game_mode, map_name, server.get("ip", "?"), str(server.get("port", "?"))]
|
||||
addr_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
addr_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
addr_lbl.add_theme_color_override("font_color", Color(0.85, 0.87, 0.92))
|
||||
|
||||
Reference in New Issue
Block a user