完善客户端功能并同步差距文档

This commit is contained in:
shenlei
2026-09-03 18:40:01 +09:00
parent f93a172296
commit 13ccb8d02d
135 changed files with 21881 additions and 1259 deletions
+86
View File
@@ -0,0 +1,86 @@
# DungeonState —— GC_DUNGEON 目的地状态(40250/ClientVS22)。
#
# 参考端 PythonNetworkStreamPhaseGame.cpp:3875-3901 收到目的地后调用
# CPythonPlayer::SetDungeonDestinationPositionPythonPlayer.cpp:147-167、
# :1255-1285 保存全局像素/厘米坐标、立即提示,并在接近目标后清除,未到达时
# 每 20 秒再次提示。本工程把目标状态与 UI 解耦,UI 只消费信号。
extends Node
signal destination_changed(active: bool, world_pos: Vector3)
signal destination_reached(world_pos: Vector3)
signal alarm_requested(world_pos: Vector3)
const TIME_ATTACK_START := 0
const DESTINATION_POSITION := 1
const ARRIVAL_THRESHOLD_CM := 10000.0
const ALARM_INTERVAL_SEC := 20.0
var client: Node
var player_getter: Callable
var active := false
var destination_cm := Vector2.ZERO
var destination_world := Vector3.ZERO
var last_subheader := -1
var alarm_count := 0
var _last_alarm_ms := -1
func setup(m2client: Node, get_player: Callable) -> void:
client = m2client
player_getter = get_player
if client and client.has_signal("dungeon_event"):
client.dungeon_event.connect(_on_dungeon_event)
if client and client.has_signal("world_reset"):
client.world_reset.connect(clear)
func _on_dungeon_event(subheader: int, x: int, y: int, has_destination: bool) -> void:
last_subheader = subheader
# ClientVS22's TIME_ATTACK_START branch is intentionally empty. Consume the
# packet but do not invent a local timer that the reference client does not run.
if subheader != DESTINATION_POSITION or not has_destination:
return
destination_cm = Vector2(float(x), float(y))
# GC_DUNGEON carries TPixelPosition x/y in global server centimetres. The
# network frame uses -Y for Godot Z before MapCoord applies the map base.
destination_world = MapCoord.to_world(Vector3(float(x) * 0.01, 0.0, -float(y) * 0.01))
active = true
_last_alarm_ms = -1
destination_changed.emit(true, destination_world)
_alarm()
func clear() -> void:
var was_active := active
active = false
destination_cm = Vector2.ZERO
destination_world = Vector3.ZERO
_last_alarm_ms = -1
if was_active:
destination_changed.emit(false, Vector3.ZERO)
func _player() -> Node3D:
if not player_getter.is_valid():
return null
var p: Variant = player_getter.call()
return p as Node3D
func _alarm() -> void:
_last_alarm_ms = Time.get_ticks_msec()
alarm_count += 1
alarm_requested.emit(destination_world)
func _process(_dt: float) -> void:
if not active:
return
var p := _player()
if p:
# Match PythonPlayer.cpp:155: integer Manhattan distance, not Euclidean.
var p_cm := MapCoord.to_server_cm(p.global_position)
var distance_cm := absf(p_cm.x - destination_cm.x) + absf(p_cm.y - destination_cm.y)
if distance_cm < ARRIVAL_THRESHOLD_CM:
var reached := destination_world
active = false
destination_changed.emit(false, reached)
destination_reached.emit(reached)
return
var now := Time.get_ticks_msec()
if _last_alarm_ms < 0 or now - _last_alarm_ms > int(ALARM_INTERVAL_SEC * 1000.0):
_alarm()