Верити: второй преследователь, общая сеть преследователей

- models/verity: модель verity2, звук из видео (verity.ogg), сцена с тем же
  мозгом, что у камня. В Backrooms вместо камня — Верити; в песочнице — Верити и камень.
- Сеть: синхронизация любого числа преследователей по имени (Net.chasers,
  submit_chasers/recv_chasers) вместо одного камня.
- rock-follow.gd: chaser_id, model_scale, emission_energy (чисто светящийся материал).
- Экспорт: models/verity только в пакетах карт.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 17:20:27 +05:00
co-authored by Claude Sonnet 5.5
parent af81050a1a
commit ed4ee86c8e
16 changed files with 288 additions and 27 deletions
+22 -15
View File
@@ -27,12 +27,10 @@ var connected := false
var current_map := "world"
var map_ready := false
# Камень-преследователь: его считает один клиент (владелец), остальные повторяют
var rock: Node3D = null # Камень этой сцены (регистрирует сам)
# Преследователи (камень, Верити…): их все считает один клиент (владелец), остальные повторяют
var chasers := {} # id -> Node3D; преследователи этой сцены, регистрируют себя сами
var chaser_states := {} # id -> {"pos": Vector3, "yaw": float} — что прислал владелец
var rock_owner := 0 # peer_id владельца (0 — ещё не назначен)
var rock_pos := Vector3.ZERO
var rock_yaw := 0.0
var rock_has_state := false
var dead_peers := {} # peer_id -> true, пока игрока поймали
var hooked_player: Node = null
@@ -142,9 +140,9 @@ func submit_state(pos: Vector3, yaw: float, pitch: float, flags: int) -> void:
for other in _room(peer_maps.get(sender, ""), sender):
recv_state.rpc_id(other, sender, pos, yaw, pitch, flags)
# Владелец камня -> сервер: позиция камня. Сервер пересылает остальным на той же карте.
# Владелец -> сервер: позиции всех преследователей. Сервер пересылает остальным на той же карте.
@rpc("any_peer", "unreliable_ordered")
func submit_rock(pos: Vector3, yaw: float) -> void:
func submit_chasers(ids: PackedStringArray, positions: PackedVector3Array, yaws: PackedFloat32Array) -> void:
if not is_server:
return
var sender := multiplayer.get_remote_sender_id()
@@ -153,7 +151,7 @@ func submit_rock(pos: Vector3, yaw: float) -> void:
return
rock_seen[map] = Time.get_ticks_msec() / 1000.0
for other in _room(map, sender):
recv_rock.rpc_id(other, pos, yaw)
recv_chasers.rpc_id(other, ids, positions, yaws)
# Клиент -> сервер: его поймали (dead) или он возродился в точке pos.
@rpc("any_peer", "reliable")
@@ -210,7 +208,7 @@ func _reset_room_state() -> void:
ghosts.clear()
dead_peers.clear()
rock_owner = 0
rock_has_state = false
chaser_states.clear()
hooked_player = null
map_ready = false
@@ -245,12 +243,11 @@ func recv_rock_owner(id: int) -> void:
rock_owner = id
@rpc("authority", "unreliable_ordered")
func recv_rock(pos: Vector3, yaw: float) -> void:
func recv_chasers(ids: PackedStringArray, positions: PackedVector3Array, yaws: PackedFloat32Array) -> void:
if not map_ready:
return
rock_pos = pos
rock_yaw = yaw
rock_has_state = true
for i in ids.size():
chaser_states[ids[i]] = {"pos": positions[i], "yaw": yaws[i]}
@rpc("authority", "reliable")
func recv_life(id: int, dead: bool, pos: Vector3) -> void:
@@ -309,5 +306,15 @@ func _process(delta: float) -> void:
return
_hook_player(player)
submit_state.rpc_id(1, player.global_position, player.rotation.y, player.head.rotation.x, player.get_net_flags())
if rock != null and rock_is_local():
submit_rock.rpc_id(1, rock.global_position, rock.rotation.y)
if not chasers.is_empty() and rock_is_local():
var ids := PackedStringArray()
var positions := PackedVector3Array()
var yaws := PackedFloat32Array()
for id in chasers:
var c: Node3D = chasers[id]
if is_instance_valid(c):
ids.append(id)
positions.append(c.global_position)
yaws.append(c.rotation.y)
if not ids.is_empty():
submit_chasers.rpc_id(1, ids, positions, yaws)