Add in-game chat, bot fill for casual, and borderless fullscreen
- In-game chat: T=all, Y=team (race doubles as team), server-relayed and team-filtered via new ChatManager autoload; last 10 messages shown bottom-left (chat/chat_box.gd). - Bot fill for casual (bots/): keeps each of the match's 2 offered races at a minimum of 7 total humans+bots, spawning/despawning reactively as players join/leave. Bots always fly their race's fighter and use negative peer_ids so they ride the existing networked-ship stack (spawning, loadout sync, health/position sync, bullet attribution) with no special-casing. Casual matchmaking now forms with just 1 real player queued instead of waiting for a second (matchmaking-api/). - Borderless fullscreen with stretch scaling disabled instead of scaling the Steam-Deck-matched 1280x800 canvas up to fill PC monitors (which read as zoomed in) — PC monitors now reveal more world/HUD at native size instead. Reworked main_menu/team_select/chat_box to position via anchors relative to the real window instead of hardcoded coordinates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -107,9 +107,11 @@ const RACES := [
|
||||
},
|
||||
]
|
||||
|
||||
# Row layout constants (screen is 1280×800)
|
||||
const ROW_X1 := 120.0
|
||||
const ROW_X2 := 1160.0
|
||||
# Row layout constants — ROW_X1/ROW_X2_MARGIN are left/right margins (not
|
||||
# absolute x-coordinates), so rows stretch to fill the actual window width;
|
||||
# stretch mode is disabled (project.godot), so nothing scales automatically.
|
||||
const ROW_X1 := 120.0
|
||||
const ROW_X2_MARGIN := 120.0
|
||||
const ROW_H := 115.0
|
||||
const ROW_GAP := 6.0
|
||||
const ROW_START := 175.0
|
||||
@@ -166,7 +168,7 @@ func _build_root() -> void:
|
||||
_root.add_child(overlay)
|
||||
|
||||
_loading_lbl = _label(_root, "LOADING MATCH...", 20, Color(0.5, 0.6, 0.75),
|
||||
0, 380, 1280, 420, HORIZONTAL_ALIGNMENT_CENTER)
|
||||
0, 380, 0, 420, HORIZONTAL_ALIGNMENT_CENTER, 0.0, 1.0)
|
||||
|
||||
|
||||
func _swap_content() -> Control:
|
||||
@@ -185,18 +187,24 @@ func _show_race_selection() -> void:
|
||||
var c := _swap_content()
|
||||
|
||||
_label(c, "CHOOSE YOUR FACTION", 32, Color(0.42, 0.87, 1.0),
|
||||
0, 105, 1280, 160, HORIZONTAL_ALIGNMENT_CENTER)
|
||||
0, 105, 0, 160, HORIZONTAL_ALIGNMENT_CENTER, 0.0, 1.0)
|
||||
_label(c, "This match: %s vs %s" % [_offered[0].name, _offered[1].name],
|
||||
13, Color(0.45, 0.58, 0.72),
|
||||
0, 170, 1280, 200, HORIZONTAL_ALIGNMENT_CENTER)
|
||||
0, 170, 0, 200, HORIZONTAL_ALIGNMENT_CENTER, 0.0, 1.0)
|
||||
|
||||
# Both race buttons are a fixed 440px wide with a 100px gap, centered on
|
||||
# the actual screen width via anchor 0.5 — not a hardcoded 1280px design
|
||||
# width, since stretch mode is disabled (see project.godot).
|
||||
const BTN_W := 440.0
|
||||
const HALF_GAP := 50.0
|
||||
|
||||
var left_btn := _make_race_btn(_offered[0])
|
||||
_place(left_btn, 150, 225, 590, 540)
|
||||
_place(left_btn, -(BTN_W + HALF_GAP), 225, -HALF_GAP, 540, 0.5, 0.5)
|
||||
left_btn.pressed.connect(_on_race_chosen.bind(_offered[0]))
|
||||
c.add_child(left_btn)
|
||||
|
||||
var right_btn := _make_race_btn(_offered[1])
|
||||
_place(right_btn, 690, 225, 1130, 540)
|
||||
_place(right_btn, HALF_GAP, 225, BTN_W + HALF_GAP, 540, 0.5, 0.5)
|
||||
right_btn.pressed.connect(_on_race_chosen.bind(_offered[1]))
|
||||
c.add_child(right_btn)
|
||||
|
||||
@@ -212,15 +220,19 @@ func _show_ship_selection() -> void:
|
||||
var c := _swap_content()
|
||||
|
||||
_label(c, "SELECT YOUR SHIP", 30, Color(0.42, 0.87, 1.0),
|
||||
0, 85, 1280, 130, HORIZONTAL_ALIGNMENT_CENTER)
|
||||
0, 85, 0, 130, HORIZONTAL_ALIGNMENT_CENTER, 0.0, 1.0)
|
||||
_label(c, "Playing as — " + _chosen_race.name, 14, _chosen_race.color,
|
||||
0, 135, 1280, 165, HORIZONTAL_ALIGNMENT_CENTER)
|
||||
0, 135, 0, 165, HORIZONTAL_ALIGNMENT_CENTER, 0.0, 1.0)
|
||||
|
||||
var ships: Array = _chosen_race.ships
|
||||
for i in ships.size():
|
||||
var y := ROW_START + i * (ROW_H + ROW_GAP)
|
||||
var btn := _make_ship_row(ships[i], _chosen_race.color)
|
||||
_place(btn, ROW_X1, y, ROW_X2, y + ROW_H)
|
||||
# ROW_X1 margin from the left edge, ROW_X2_MARGIN margin from the
|
||||
# actual right edge (anchor_right=1.0) — the row stretches to fill
|
||||
# whatever width is available instead of stopping at a hardcoded
|
||||
# 1280px design width.
|
||||
_place(btn, ROW_X1, y, -ROW_X2_MARGIN, y + ROW_H, 0.0, 1.0)
|
||||
btn.pressed.connect(_on_ship_chosen.bind(ships[i]))
|
||||
c.add_child(btn)
|
||||
|
||||
@@ -368,23 +380,44 @@ func _make_ship_row(ship: Dictionary, accent: Color) -> Button:
|
||||
|
||||
func _label(parent: Control, text: String, font_size: int, color: Color,
|
||||
x1: float, y1: float, x2: float, y2: float,
|
||||
align: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT) -> Label:
|
||||
align: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT,
|
||||
anchor_left: float = 0.0, anchor_right: float = 0.0) -> Label:
|
||||
var lbl := Label.new()
|
||||
lbl.text = text
|
||||
lbl.horizontal_alignment = align
|
||||
lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
lbl.add_theme_font_size_override("font_size", font_size)
|
||||
lbl.add_theme_color_override("font_color", color)
|
||||
_place(lbl, x1, y1, x2, y2)
|
||||
_place(lbl, x1, y1, x2, y2, anchor_left, anchor_right)
|
||||
parent.add_child(lbl)
|
||||
return lbl
|
||||
|
||||
|
||||
func _place(node: Control, x1: float, y1: float, x2: float, y2: float) -> void:
|
||||
node.set_anchor_and_offset(SIDE_LEFT, 0.0, x1)
|
||||
node.set_anchor_and_offset(SIDE_TOP, 0.0, y1)
|
||||
node.set_anchor_and_offset(SIDE_RIGHT, 0.0, x2)
|
||||
node.set_anchor_and_offset(SIDE_BOTTOM, 0.0, y2)
|
||||
# anchor_left/anchor_right default to 0.0 (pixels measured from the left
|
||||
# edge), matching the design-resolution layout this screen was originally
|
||||
# built at. Pass 1.0 for anchor_right to make x2 a margin from the actual
|
||||
# right edge instead (stretches with window width), or 0.5 for both to
|
||||
# center a fixed-size element regardless of window width — see call sites
|
||||
# below for which each element needs (stretch mode is disabled, so nothing
|
||||
# scales automatically the way it used to under canvas_items stretch).
|
||||
#
|
||||
# Anchors and offsets are set as plain property assignments, NOT via
|
||||
# sequential set_anchor_and_offset() calls — that method's default
|
||||
# push_opposite_anchor=true drags the opposite side's anchor/offset along
|
||||
# whenever the two calls momentarily disagree (e.g. left set to 0.5 while
|
||||
# right is still its 0.0 default), corrupting layout for any non-0.0 anchor.
|
||||
# Found via main_menu.gd's play buttons rendering flush against the left
|
||||
# edge instead of centered.
|
||||
func _place(node: Control, x1: float, y1: float, x2: float, y2: float,
|
||||
anchor_left: float = 0.0, anchor_right: float = 0.0) -> void:
|
||||
node.anchor_left = anchor_left
|
||||
node.anchor_right = anchor_right
|
||||
node.anchor_top = 0.0
|
||||
node.anchor_bottom = 0.0
|
||||
node.offset_left = x1
|
||||
node.offset_top = y1
|
||||
node.offset_right = x2
|
||||
node.offset_bottom = y2
|
||||
|
||||
|
||||
func _flat(col: Color, radius: int = 0) -> StyleBoxFlat:
|
||||
|
||||
Reference in New Issue
Block a user