M2.4: the server decides online (characters, inventory, stats, death, owned graves, per-player saves); death screen with Respawn after 3 s

This commit is contained in:
Paul Bailey
2026-11-04 19:18:31 +00:00
parent 8e8f10eb96
commit 35a656e996
16 changed files with 1124 additions and 477 deletions

View File

@@ -209,6 +209,72 @@ func fade(mid: Callable) -> void:
player.ui_open = false
black.queue_free())
var _death: Control
## The death screen: the world fades to dark red, it says what killed you
## and where your things are, and after `delay` seconds Respawn (or Enter)
## calls `respawn`.
func show_death(cause: String, note: String, delay: float, respawn: Callable) -> void:
hide_death()
player.ui_open = true
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
var bg := ColorRect.new()
bg.color = Color(0.22, 0.02, 0.02, 0.0)
bg.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
bg.mouse_filter = Control.MOUSE_FILTER_STOP
add_child(bg)
_death = bg
var col := VBoxContainer.new()
col.set_anchors_and_offsets_preset(Control.PRESET_CENTER)
col.grow_horizontal = Control.GROW_DIRECTION_BOTH
col.grow_vertical = Control.GROW_DIRECTION_BOTH
col.alignment = BoxContainer.ALIGNMENT_CENTER
col.add_theme_constant_override("separation", 14)
col.modulate.a = 0.0
bg.add_child(col)
var title := UITheme.label("You died", 56)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
col.add_child(title)
var why := UITheme.label(cause if not cause.is_empty() else "You died", 22)
why.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
col.add_child(why)
var where := UITheme.label(note, 16, UITheme.TEXT_DIM)
where.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
col.add_child(where)
var btn := Button.new()
btn.custom_minimum_size = Vector2(220, 46)
btn.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
btn.disabled = true
btn.text = "Respawn (%d)" % ceili(delay)
col.add_child(btn)
btn.pressed.connect(func():
hide_death()
respawn.call())
var tw := bg.create_tween()
tw.tween_property(bg, "color:a", 0.72, 0.9)
tw.parallel().tween_property(col, "modulate:a", 1.0, 0.9)
# Count down, then let them go.
for k in range(1, ceili(delay)):
var left := ceili(delay) - k
get_tree().create_timer(float(k)).timeout.connect(func():
if is_instance_valid(btn) and btn.disabled:
btn.text = "Respawn (%d)" % left)
get_tree().create_timer(delay).timeout.connect(func():
if is_instance_valid(btn):
btn.disabled = false
btn.text = "Respawn"
btn.grab_focus())
func is_dead_screen() -> bool:
return _death != null and is_instance_valid(_death)
func hide_death() -> void:
if is_dead_screen():
_death.queue_free()
_death = null
player.ui_open = false
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
## Closes the inventory / menu (e.g. on death).
func close_screens() -> void:
if _screen.is_open():
@@ -221,6 +287,8 @@ func _on_inventory_requested(container: Node) -> void:
## ESC: close the inventory if it's open, otherwise toggle the pause menu.
func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_ESCAPE:
if is_dead_screen():
return # (respawn first)
if _screen.is_open():
_screen.close()
elif _menu.is_open():