# DungeonState —— GC_DUNGEON 目的地状态(40250/ClientVS22)。 # # 参考端 PythonNetworkStreamPhaseGame.cpp:3875-3901 收到目的地后调用 # CPythonPlayer::SetDungeonDestinationPosition;PythonPlayer.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()