Startmenü, fremde Figuren und Statuszeile
Das Startmenü fragt nach dem Namen und bietet allein spielen, die Welt öffnen oder beitreten. Die Figuren der anderen Spieler gleiten zu ihrer neuesten Position und tragen ihren Namen. Die Anzeige füllt Werte in Meldungen ein und hat eine Statuszeile. Die Texte für Menü und Netzwerk gibt es auf Deutsch und Englisch. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
class_name Figures
|
||||
extends Node2D
|
||||
## The figures of the other players, with their names. A new position comes
|
||||
## over the network about ten times a second; in between, each figure glides
|
||||
## toward the latest one.
|
||||
|
||||
## How quickly a figure closes the gap to its latest position, per second.
|
||||
const GLIDE := 15.0
|
||||
const COLOR := Color(0.55, 0.8, 0.95)
|
||||
const OUTLINE := Color(0.05, 0.1, 0.15)
|
||||
const NAME_SIZE := 8
|
||||
const NAME_WIDTH := 80.0
|
||||
|
||||
var session: GwSession
|
||||
var _shown: Dictionary[int, Vector2] = {}
|
||||
var _latest: Dictionary[int, Vector2] = {}
|
||||
|
||||
|
||||
## Moves the figure of `player` toward `position`. A figure that was not
|
||||
## shown yet appears there at once.
|
||||
func place(player: int, position: Vector2) -> void:
|
||||
if not _shown.has(player):
|
||||
_shown[player] = position
|
||||
_latest[player] = position
|
||||
queue_redraw()
|
||||
|
||||
|
||||
## Removes the figure of `player`.
|
||||
func forget(player: int) -> void:
|
||||
_shown.erase(player)
|
||||
_latest.erase(player)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func clear() -> void:
|
||||
_shown.clear()
|
||||
_latest.clear()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
## Where the figure of `player` is shown, or Vector2.INF if it is not.
|
||||
func shown_at(player: int) -> Vector2:
|
||||
return _shown.get(player, Vector2.INF)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _shown.is_empty():
|
||||
return
|
||||
var weight := minf(1.0, GLIDE * delta)
|
||||
for player in _shown:
|
||||
_shown[player] = _shown[player].lerp(_latest[player], weight)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var font := ThemeDB.fallback_font
|
||||
for player in _shown:
|
||||
var at := _shown[player]
|
||||
draw_circle(at, Avatar.RADIUS + 1.0, OUTLINE)
|
||||
draw_circle(at, Avatar.RADIUS, COLOR)
|
||||
if session != null:
|
||||
var label := Vector2(at.x - NAME_WIDTH / 2.0, at.y - Avatar.RADIUS - 3.0)
|
||||
draw_string(font, label, session.player_name(player), HORIZONTAL_ALIGNMENT_CENTER, NAME_WIDTH, NAME_SIZE)
|
||||
@@ -0,0 +1 @@
|
||||
uid://0krucvyguufx
|
||||
+21
-5
@@ -1,11 +1,13 @@
|
||||
class_name Hud
|
||||
extends CanvasLayer
|
||||
## Text over the game: the stone count, short messages and a help line.
|
||||
## Text over the game: the stone count, short messages, a status line for
|
||||
## playing together and a help line.
|
||||
|
||||
const MESSAGE_SECONDS := 2.5
|
||||
|
||||
var _stone := Label.new()
|
||||
var _message := Label.new()
|
||||
var _status := Label.new()
|
||||
var _help := Label.new()
|
||||
var _message_left := 0.0
|
||||
|
||||
@@ -15,9 +17,12 @@ func _ready() -> void:
|
||||
_stone.add_theme_font_size_override("font_size", 22)
|
||||
_message.position = Vector2(16, 48)
|
||||
_message.add_theme_font_size_override("font_size", 18)
|
||||
_status.add_theme_font_size_override("font_size", 14)
|
||||
_status.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT, Control.PRESET_MODE_MINSIZE, 16)
|
||||
_status.grow_horizontal = Control.GROW_DIRECTION_BEGIN
|
||||
_help.text = tr("HELP")
|
||||
_help.add_theme_font_size_override("font_size", 14)
|
||||
for label: Label in [_stone, _message, _help]:
|
||||
for label: Label in [_stone, _message, _status, _help]:
|
||||
label.add_theme_color_override("font_outline_color", Color.BLACK)
|
||||
label.add_theme_constant_override("outline_size", 4)
|
||||
add_child(label)
|
||||
@@ -36,9 +41,10 @@ func show_stone(count: int) -> void:
|
||||
_stone.text = tr("STONE_COUNT") % count
|
||||
|
||||
|
||||
## Shows the text for `key` for a few seconds.
|
||||
func flash(key: String) -> void:
|
||||
_message.text = tr(key)
|
||||
## Shows the text for `key` for a few seconds, with `values` filled into
|
||||
## its placeholders.
|
||||
func flash(key: String, values: Array = []) -> void:
|
||||
_message.text = tr(key) % values if not values.is_empty() else tr(key)
|
||||
_message_left = MESSAGE_SECONDS
|
||||
|
||||
|
||||
@@ -48,6 +54,16 @@ func show_paused(paused: bool) -> void:
|
||||
_message_left = 0.0
|
||||
|
||||
|
||||
## Shows `text` in the top right corner until it is replaced.
|
||||
func show_status(text: String) -> void:
|
||||
_status.text = text
|
||||
|
||||
|
||||
## The text currently shown in the status line. Tests read it.
|
||||
func status_text() -> String:
|
||||
return _status.text
|
||||
|
||||
|
||||
## The text currently shown as a message. Tests read it.
|
||||
func message() -> String:
|
||||
return _message.text
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
class_name Menu
|
||||
extends CanvasLayer
|
||||
## The start screen: the player's name, then playing alone, opening the world
|
||||
## for guests, or joining a host.
|
||||
|
||||
signal play_chosen(open: bool)
|
||||
signal join_chosen(address: String)
|
||||
|
||||
var name_field := LineEdit.new()
|
||||
var address_field := LineEdit.new()
|
||||
var solo_button := Button.new()
|
||||
var host_button := Button.new()
|
||||
var join_button := Button.new()
|
||||
var _status := Label.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var center := CenterContainer.new()
|
||||
center.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(center)
|
||||
var panel := PanelContainer.new()
|
||||
center.add_child(panel)
|
||||
var box := VBoxContainer.new()
|
||||
box.custom_minimum_size = Vector2(380, 0)
|
||||
box.add_theme_constant_override("separation", 10)
|
||||
panel.add_child(box)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "Grovewright"
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title.add_theme_font_size_override("font_size", 32)
|
||||
name_field.placeholder_text = tr("MENU_NAME")
|
||||
name_field.max_length = 32
|
||||
solo_button.text = tr("MENU_SOLO")
|
||||
host_button.text = tr("MENU_HOST")
|
||||
var row := HBoxContainer.new()
|
||||
for button in [solo_button, host_button]:
|
||||
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_child(button)
|
||||
address_field.placeholder_text = tr("MENU_ADDRESS")
|
||||
join_button.text = tr("MENU_JOIN")
|
||||
_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_status.custom_minimum_size = Vector2(380, 0)
|
||||
for control: Control in [title, name_field, row, HSeparator.new(), address_field, join_button, _status]:
|
||||
box.add_child(control)
|
||||
|
||||
solo_button.pressed.connect(func() -> void: play_chosen.emit(false))
|
||||
host_button.pressed.connect(func() -> void: play_chosen.emit(true))
|
||||
join_button.pressed.connect(func() -> void: join_chosen.emit(address_field.text.strip_edges()))
|
||||
address_field.text_submitted.connect(func(_text: String) -> void: join_button.pressed.emit())
|
||||
|
||||
|
||||
## Shows `text` below the buttons.
|
||||
func show_status(text: String) -> void:
|
||||
_status.text = text
|
||||
|
||||
|
||||
## The text shown below the buttons. Tests read it.
|
||||
func status() -> String:
|
||||
return _status.text
|
||||
|
||||
|
||||
## The name the player typed, without spaces around it.
|
||||
func player_name() -> String:
|
||||
return name_field.text.strip_edges()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dppcwwh67rtu6
|
||||
Reference in New Issue
Block a user