Files

87 lines
3.0 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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()